Bug fix
[kopensolaris-gnu/glibc.git] / sysdeps / x86_64 / strcpy_chk.S
1 /* strcpy/stpcpy checking implementation for x86-64.
2    Copyright (C) 2002, 2004 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Andreas Jaeger <aj@suse.de>, 2002.
5    Adopted into checking version by Jakub Jelinek <jakub@redhat.com>.
6
7    The GNU C Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2.1 of the License, or (at your option) any later version.
11
12    The GNU C Library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with the GNU C Library; if not, write to the Free
19    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20    02111-1307 USA.  */
21
22 #include <sysdep.h>
23 #include "asm-syntax.h"
24 #include "bp-sym.h"
25 #include "bp-asm.h"
26
27 #ifndef USE_AS_STPCPY_CHK
28 # define STRCPY_CHK __strcpy_chk
29 #endif
30
31         .text
32 ENTRY (STRCPY_CHK)
33         movq    %rsi, %rcx      /* Source register. */
34         andl    $7, %ecx        /* mask alignment bits */
35 #ifndef USE_AS_STPCPY_CHK
36         movq    %rdi, %r10      /* Duplicate destination pointer.  */
37 #endif
38         jz 5f                   /* aligned => start loop */
39
40         cmpq    $8, %rdx        /* Check if only few bytes left in
41                                    destination.  */
42         jb      50f
43
44         subq    $8, %rcx        /* We need to align to 8 bytes.  */
45         addq    %rcx, %rdx      /* Subtract count of stored bytes
46                                    in the cycle below from destlen.  */
47
48         /* Search the first bytes directly.  */
49 0:
50         movb    (%rsi), %al     /* Fetch a byte */
51         testb   %al, %al        /* Is it NUL? */
52         movb    %al, (%rdi)     /* Store it */
53         jz      4f              /* If it was NUL, done! */
54         incq    %rsi
55         incq    %rdi
56         incl    %ecx
57         jnz     0b
58
59 5:
60         movq $0xfefefefefefefeff,%r8
61         cmpq    $32, %rdx       /* Are there enough bytes in destination
62                                    for the next unrolled round?  */
63         jb      60f             /* If not, avoid the unrolled loop.  */
64
65         /* Now the sources is aligned.  Unfortunatly we cannot force
66            to have both source and destination aligned, so ignore the
67            alignment of the destination.  */
68         .p2align 4
69 1:
70         /* 1st unroll.  */
71         movq    (%rsi), %rax    /* Read double word (8 bytes).  */
72         addq    $8, %rsi        /* Adjust pointer for next word.  */
73         movq    %rax, %r9       /* Save a copy for NUL finding.  */
74         addq    %r8, %r9        /* add the magic value to the word.  We get
75                                    carry bits reported for each byte which
76                                    is *not* 0 */
77         jnc     3f              /* highest byte is NUL => return pointer */
78         xorq    %rax, %r9       /* (word+magic)^word */
79         orq     %r8, %r9        /* set all non-carry bits */
80         incq    %r9             /* add 1: if one carry bit was *not* set
81                                    the addition will not result in 0.  */
82
83         jnz     3f              /* found NUL => return pointer */
84
85         movq    %rax, (%rdi)    /* Write value to destination.  */
86         addq    $8, %rdi        /* Adjust pointer.  */
87
88         /* 2nd unroll.  */
89         movq    (%rsi), %rax    /* Read double word (8 bytes).  */
90         addq    $8, %rsi        /* Adjust pointer for next word.  */
91         movq    %rax, %r9       /* Save a copy for NUL finding.  */
92         addq    %r8, %r9        /* add the magic value to the word.  We get
93                                    carry bits reported for each byte which
94                                    is *not* 0 */
95         jnc     3f              /* highest byte is NUL => return pointer */
96         xorq    %rax, %r9       /* (word+magic)^word */
97         orq     %r8, %r9        /* set all non-carry bits */
98         incq    %r9             /* add 1: if one carry bit was *not* set
99                                    the addition will not result in 0.  */
100
101         jnz     3f              /* found NUL => return pointer */
102
103         movq    %rax, (%rdi)    /* Write value to destination.  */
104         addq    $8, %rdi        /* Adjust pointer.  */
105
106         /* 3rd unroll.  */
107         movq    (%rsi), %rax    /* Read double word (8 bytes).  */
108         addq    $8, %rsi        /* Adjust pointer for next word.  */
109         movq    %rax, %r9       /* Save a copy for NUL finding.  */
110         addq    %r8, %r9        /* add the magic value to the word.  We get
111                                    carry bits reported for each byte which
112                                    is *not* 0 */
113         jnc     3f              /* highest byte is NUL => return pointer */
114         xorq    %rax, %r9       /* (word+magic)^word */
115         orq     %r8, %r9        /* set all non-carry bits */
116         incq    %r9             /* add 1: if one carry bit was *not* set
117                                    the addition will not result in 0.  */
118
119         jnz     3f              /* found NUL => return pointer */
120
121         movq    %rax, (%rdi)    /* Write value to destination.  */
122         addq    $8, %rdi        /* Adjust pointer.  */
123
124         /* 4th unroll.  */
125         movq    (%rsi), %rax    /* Read double word (8 bytes).  */
126         addq    $8, %rsi        /* Adjust pointer for next word.  */
127         movq    %rax, %r9       /* Save a copy for NUL finding.  */
128         addq    %r8, %r9        /* add the magic value to the word.  We get
129                                    carry bits reported for each byte which
130                                    is *not* 0 */
131         jnc     3f              /* highest byte is NUL => return pointer */
132         xorq    %rax, %r9       /* (word+magic)^word */
133         orq     %r8, %r9        /* set all non-carry bits */
134         incq    %r9             /* add 1: if one carry bit was *not* set
135                                    the addition will not result in 0.  */
136
137         jnz     3f              /* found NUL => return pointer */
138
139         subq    $32, %rdx       /* Adjust destlen.  */
140         movq    %rax, (%rdi)    /* Write value to destination.  */
141         addq    $8, %rdi        /* Adjust pointer.  */
142         cmpq    $32, %rdx       /* Are there enough bytes in destination
143                                    for the next unrolled round?  */
144         jae     1b              /* Next iteration.  */
145
146 60:
147         cmpq    $8, %rdx        /* Are there enough bytes in destination
148                                    for the next unrolled round?  */
149         jb      50f             /* Now, copy and check byte by byte.  */
150
151         movq    (%rsi), %rax    /* Read double word (8 bytes).  */
152         addq    $8, %rsi        /* Adjust pointer for next word.  */
153         movq    %rax, %r9       /* Save a copy for NUL finding.  */
154         addq    %r8, %r9        /* add the magic value to the word.  We get
155                                    carry bits reported for each byte which
156                                    is *not* 0 */
157         jnc     3f              /* highest byte is NUL => return pointer */
158         xorq    %rax, %r9       /* (word+magic)^word */
159         orq     %r8, %r9        /* set all non-carry bits */
160         incq    %r9             /* add 1: if one carry bit was *not* set
161                                    the addition will not result in 0.  */
162
163         jnz     3f              /* found NUL => return pointer */
164
165         subq    $8, %rdx        /* Adjust destlen.  */
166         movq    %rax, (%rdi)    /* Write value to destination.  */
167         addq    $8, %rdi        /* Adjust pointer.  */
168         jmp     60b             /* Next iteration.  */
169
170         /* Do the last few bytes. %rax contains the value to write.
171            The loop is unrolled twice.  */
172         .p2align 4
173 3:
174         /* Note that stpcpy needs to return with the value of the NUL
175            byte.  */
176         movb    %al, (%rdi)     /* 1st byte.  */
177         testb   %al, %al        /* Is it NUL.  */
178         jz      4f              /* yes, finish.  */
179         incq    %rdi            /* Increment destination.  */
180         movb    %ah, (%rdi)     /* 2nd byte.  */
181         testb   %ah, %ah        /* Is it NUL?.  */
182         jz      4f              /* yes, finish.  */
183         incq    %rdi            /* Increment destination.  */
184         shrq    $16, %rax       /* Shift...  */
185         jmp     3b              /* and look at next two bytes in %rax.  */
186
187 51:
188         /* Search the bytes directly, checking for overflows.  */
189         incq    %rsi
190         incq    %rdi
191         decq    %rdx
192         jz      HIDDEN_JUMPTARGET (__chk_fail)
193 52:
194         movb    (%rsi), %al     /* Fetch a byte */
195         testb   %al, %al        /* Is it NUL? */
196         movb    %al, (%rdi)     /* Store it */
197         jnz     51b             /* If it was NUL, done! */
198 4:
199 #ifdef USE_AS_STPCPY_CHK
200         movq    %rdi, %rax      /* Destination is return value.  */
201 #else
202         movq    %r10, %rax      /* Source is return value.  */
203 #endif
204         retq
205
206 50:
207         testq   %rdx, %rdx
208         jnz     52b
209         jmp     HIDDEN_JUMPTARGET (__chk_fail)
210
211 END (STRCPY_CHK)