| /** |
| * This file has no copyright assigned and is placed in the Public Domain. |
| * This file is part of the mingw-w64 runtime package. |
| * No warranty is given; refer to the file DISCLAIMER.PD within this package. |
| */ |
| |
| /* vsscanf, vswscanf, vfscanf, and vfwscanf all come here for i386 and arm. |
| |
| The goal of this routine is to turn a call to v*scanf into a call to |
| s*scanf. This is needed because mingw-w64 uses msvcr100.dll, which doesn't |
| support the v*scanf functions instead of msvcr120.dll which does. |
| Unfortunately, there is no defined way to know exactly how big a va_list |
| is, so we use a hard-coded buffer. |
| |
| I suppose a sufficiently-motivated person could try to parse the format |
| to figure out how many tokens there are... */ |
| |
| /* The function prototype here is (essentially): |
| |
| int __ms_vsscanf_internal (void *s, |
| void *format, |
| void *arg, |
| void *func); |
| |
| I say 'essentially' because passing a function pointer as void in ISO |
| is not supported. But in the end, I take the first parameter (which |
| may be a char *, a wchar_t *, or a FILE *) and put it into the newly |
| formed stack, and eventually call the address in func. */ |
| |
| #if defined (__x86_64__) |
| |
| .text |
| .align 16 |
| |
| /* scl 2: C_EXT - External (public) symbol - covers globals and externs |
| type 32: DT_FCN - function returning T |
| */ |
| .def __argtos; .scl 2; .type 32; .endef |
| |
| /* The max number of pointers we support. Must be an even number |
| to keep the 64bit stack 16byte aligned. Must not be less than 4. */ |
| .equ entries, 30 |
| |
| /* 64bit pointers are 8 bytes. */ |
| .equ sizeof, 8 |
| |
| /* Size of our buffer. */ |
| .equ iBytes, entries * sizeof |
| |
| /* Stack space for first 2 args to s*scanf. */ |
| .equ iOffset, (2 * sizeof) |
| |
| .seh_proc __argtos |
| __argtos: |
| |
| /* When we are done: |
| - s must be in rcx. That's where it is on entry. |
| - format must be in rdx. That's where it is on entry. |
| - The first pointer in arg must be in r8. arg is in r8 on entry. |
| - The second pointer in arg must be in r9. arg is in r8 on entry. |
| - The ($entries - 2) other pointers in arg must be on the stack, |
| starting 32bytes into rsp. */ |
| |
| /* We need enough room to shadow (s + format) |
| + (enough room for all the other args). */ |
| subq $(iOffset + iBytes), %rsp |
| .seh_stackalloc iOffset + iBytes |
| |
| .seh_endprologue |
| |
| /* We are going to copy $entries pointers from arg to our |
| local stack. Except the first 2, since they will be |
| loaded in registers. */ |
| movq $entries - 2, %r10 /* # of ptrs to copy. */ |
| |
| /* The first 32 bytes are in registers, but by spec, space |
| must still be reserved for them on the stack. Put the |
| rest of the pointers in the stack after that. */ |
| lea 32(%rsp), %r11 /* dst. */ |
| |
| .LOOP: |
| subq $1, %r10 |
| |
| /* Use 16 to skip over the first 2 pointers. */ |
| movq 16(%r8, %r10, 8), %rax |
| movq %rax, (%r11, %r10, 8) |
| jnz .LOOP |
| |
| /* r9 contains the routine we are going to call. Since we are about to |
| overwrite it, move it somewhere safe. */ |
| movq %r9, %r10 |
| |
| /* The stack is now correctly populated, and so are rcx and rdx. |
| But we need to load the last 2 regs before making the call. */ |
| movq 0x8(%r8), %r9 /* 2nd dest location (may be garbage if only 1 arg). */ |
| movq (%r8), %r8 /* 1st dest location. */ |
| |
| /* Make the call. */ |
| callq *%r10 |
| |
| addq $(iOffset + iBytes), %rsp |
| |
| retq |
| .seh_endproc |
| |
| #elif defined (_X86_) |
| |
| .text |
| .align 16 |
| |
| /* scl 2: C_EXT - External (public) symbol - covers globals and externs |
| type 32: DT_FCN - function returning T |
| */ |
| .def __argtos; .scl 2; .type 32; .endef |
| |
| /* The max number of pointers we support. Must not be less than 1. */ |
| .equ entries, 30 |
| |
| /* 64bit pointers are 8 bytes. */ |
| .equ sizeof, 4 |
| |
| /* Size of our buffer. */ |
| .set iBytes, entries * sizeof |
| |
| /* Stack space for first 2 args to s*scanf. */ |
| .equ iOffset, (2 * sizeof) |
| |
| __argtos: |
| pushl %ebp |
| movl %esp, %ebp |
| pushl %edi |
| |
| /* Reserve enough stack space for everything. |
| |
| Stack usage will look like: |
| 4 bytes - s |
| 4 bytes - format |
| (iBytes) bytes - variable # of parameters for sscanf (all ptrs). */ |
| |
| subl $(iOffset + iBytes), %esp |
| |
| /* Write out s and format where they need to be for the sscanf call. */ |
| movl 8(%ebp), %eax |
| movl %eax, (%esp) /* s. */ |
| movl 12(%ebp), %edx |
| movl %edx, 0x4(%esp) /* format. */ |
| |
| /* We are going to copy $entries pointers from arg to our |
| local stack. */ |
| movl $entries, %ecx /* # of ptrs to copy. */ |
| lea iOffset(%esp), %edi /* dst. */ |
| movl 16(%ebp), %edx /* src. */ |
| |
| .LOOP: |
| subl $1, %ecx |
| |
| movl (%edx, %ecx, 4), %eax |
| movl %eax, (%edi, %ecx, 4) |
| jnz .LOOP |
| |
| /* The stack is now correctly populated. */ |
| |
| /* Make the call. */ |
| call *20(%ebp) |
| |
| /* Restore stack. */ |
| addl $(iOffset + iBytes), %esp |
| popl %edi |
| leave |
| |
| ret |
| |
| #elif defined (__arm__) |
| |
| .text |
| .align 2 |
| .thumb_func |
| .globl __argtos |
| |
| __argtos: |
| push {r4-r7, lr} |
| sub sp, sp, #128 |
| mov r12, r3 |
| mov r4, sp |
| |
| ldr r5, [r2], #4 |
| ldr r6, [r2], #4 |
| |
| mov r3, #116 |
| 1: ldr r7, [r2], #4 |
| str r7, [r4], #4 |
| subs r3, r3, #4 |
| bne 1b |
| |
| mov r2, r5 |
| mov r3, r6 |
| blx r12 |
| add sp, sp, #128 |
| pop {r4-r7, pc} |
| |
| #elif defined (__aarch64__) |
| |
| .text |
| .align 2 |
| .globl __argtos |
| |
| __argtos: |
| stp x29, x30, [sp, #-16]! |
| mov x29, sp |
| sub sp, sp, #256 |
| mov x9, sp |
| mov x10, x2 |
| mov x11, x3 |
| |
| ldr x2, [x10], #8 |
| ldr x3, [x10], #8 |
| ldr x4, [x10], #8 |
| ldr x5, [x10], #8 |
| ldr x6, [x10], #8 |
| ldr x7, [x10], #8 |
| |
| mov x12, #240 |
| 1: ldr x13, [x10], #8 |
| str x13, [x9], #8 |
| subs x12, x12, #8 |
| b.ne 1b |
| |
| blr x11 |
| mov sp, x29 |
| ldp x29, x30, [sp], #16 |
| ret |
| |
| #endif |