1 1.1 matt /*- 2 1.1 matt * Copyright (c) 2013 The NetBSD Foundation, Inc. 3 1.1 matt * All rights reserved. 4 1.1 matt * 5 1.1 matt * This code is derived from software contributed to The NetBSD Foundation 6 1.1 matt * by Matt Thomas of 3am Software Foundry. 7 1.1 matt * 8 1.1 matt * Redistribution and use in source and binary forms, with or without 9 1.1 matt * modification, are permitted provided that the following conditions 10 1.1 matt * are met: 11 1.1 matt * 1. Redistributions of source code must retain the above copyright 12 1.1 matt * notice, this list of conditions and the following disclaimer. 13 1.1 matt * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 matt * notice, this list of conditions and the following disclaimer in the 15 1.1 matt * documentation and/or other materials provided with the distribution. 16 1.1 matt * 17 1.1 matt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 1.1 matt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 1.1 matt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 1.1 matt * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 1.1 matt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 1.1 matt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 1.1 matt * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 1.1 matt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 1.1 matt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 1.1 matt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 1.1 matt * POSSIBILITY OF SUCH DAMAGE. 28 1.1 matt */ 29 1.1 matt 30 1.1 matt #include <machine/asm.h> 31 1.1 matt 32 1.7 andvar RCSID("$NetBSD: strcpy_arm.S,v 1.7 2024/02/08 20:51:24 andvar Exp $") 33 1.1 matt 34 1.1 matt #ifdef STRLCPY 35 1.1 matt #ifdef _LIBC 36 1.1 matt WEAK_ALIAS(strlcpy, _strlcpy) 37 1.6 christos # define FUNCNAME _strlcpy 38 1.6 christos # else 39 1.6 christos # define FUNCNAME strlcpy 40 1.6 christos # endif 41 1.1 matt #elif defined(STRNCPY) 42 1.6 christos # ifdef _LIBC 43 1.5 christos WEAK_ALIAS(strncpy, _strncpy) 44 1.6 christos # define FUNCNAME _strncpy 45 1.6 christos # else 46 1.6 christos # define FUNCNAME strncpy 47 1.6 christos # endif 48 1.1 matt #else 49 1.6 christos # ifdef _LIBC 50 1.5 christos WEAK_ALIAS(strcpy, _strcpy) 51 1.6 christos # define FUNCNAME _strcpy 52 1.6 christos # else 53 1.6 christos # define FUNCNAME strcpy 54 1.6 christos # endif 55 1.1 matt #endif 56 1.1 matt 57 1.1 matt #ifdef __ARMEL__ 58 1.1 matt #define lslo lsr /* shift to lower address */ 59 1.1 matt #define lshi lsl /* shift to higher address */ 60 1.1 matt #define BYTE0 0x000000ff 61 1.1 matt #define BYTE1 0x0000ff00 62 1.1 matt #define BYTE2 0x00ff0000 63 1.1 matt #define BYTE3 0xff000000 64 1.1 matt #else 65 1.1 matt #define lslo lsl /* shift to lower address */ 66 1.1 matt #define lshi lsr /* shift to higher address */ 67 1.1 matt #define BYTE0 0xff000000 68 1.1 matt #define BYTE1 0x00ff0000 69 1.1 matt #define BYTE2 0x0000ff00 70 1.1 matt #define BYTE3 0x000000ff 71 1.1 matt #endif 72 1.1 matt 73 1.1 matt /* 74 1.1 matt * On armv6 and later, to quickly determine if a word contains a NUL (0) byte, 75 1.1 matt * we add 254 to each byte using the UQADD8 (unsigned saturating add 8) 76 1.1 matt * instruction. For every non-NUL byte, the result for that byte will become 77 1.1 matt * 255. For NUL, it will be 254. When we complement the result of all 4 adds, 78 1.1 matt * if the result is non-0 then we must have encountered a NUL. 79 1.1 matt * 80 1.1 matt * For earlier architecture, we just use tst on all 4 bytes. There are other 81 1.1 matt * algorithms to detect NULs but they take longer and use more instructions. 82 1.1 matt */ 83 1.1 matt 84 1.1 matt /* 85 1.1 matt * char *strcpy(char *dst, const char *src); 86 1.1 matt * char *strncpy(char *dst, const char *src, size_t len); 87 1.1 matt * size_t strlcpy(char *dst, const char *src, size_t len); 88 1.1 matt */ 89 1.1 matt 90 1.1 matt .text 91 1.1 matt ENTRY(FUNCNAME) 92 1.1 matt #if defined(STRLCPY) 93 1.1 matt cmp r2, #1 /* is length 1 or less? */ 94 1.1 matt bhi 1f /* no, do normal */ 95 1.1 matt moveq r3, #0 /* = 1? load NUL */ 96 1.3 matt strbeq r3, [r0] /* = 1? write NUL to dst */ 97 1.1 matt mov r0, r1 /* move src to r0 */ 98 1.1 matt b PLT_SYM(_C_LABEL(strlen)) /* and tailcall strlen */ 99 1.1 matt 1: 100 1.1 matt sub r2, r2, #1 /* leave one byte for NUL */ 101 1.1 matt #endif 102 1.1 matt #if defined(STRNCPY) 103 1.1 matt cmp r2, #0 /* 0 length? */ 104 1.1 matt RETc(eq) /* yes, just return */ 105 1.1 matt #endif 106 1.1 matt push {r4-r9} /* save some registers */ 107 1.1 matt #ifdef _ARM_ARCH_6 108 1.1 matt #ifdef _ARM_ARCH_7 109 1.1 matt movw r7, #0xfefe /* magic constant; 254 in each byte */ 110 1.1 matt #else 111 1.1 matt mov r7, #0xfe /* put 254 in low byte */ 112 1.1 matt orr r7, r7, r7, lsl #8 /* move to next byte */ 113 1.1 matt #endif 114 1.1 matt orr r7, r7, r7, lsl #16 /* move to next halfword */ 115 1.1 matt #endif 116 1.1 matt 117 1.1 matt #if defined(STRLCPY) 118 1.1 matt add r6, r1, #1 /* save for return (deal with NUL) */ 119 1.1 matt #else 120 1.1 matt mov r6, r0 /* save for return */ 121 1.1 matt #endif 122 1.1 matt 123 1.1 matt .Ldst_align: 124 1.1 matt tst r0, #3 /* check for dst alignment */ 125 1.1 matt beq .Ldst_aligned /* ok, proceed to next check */ 126 1.1 matt ldrb r5, [r1], #1 /* load a byte */ 127 1.1 matt #if defined(STRNCPY) 128 1.1 matt subs r2, r2, #1 /* subtract out from count */ 129 1.1 matt bmi .Ldst_full /* zero? the dst has no more room */ 130 1.1 matt #endif 131 1.1 matt strb r5, [r0], #1 /* store a byte */ 132 1.1 matt teq r5, #0 /* was it a NUL? */ 133 1.1 matt beq .Lend_of_string /* yes, we are done */ 134 1.1 matt #if defined(STRLCPY) 135 1.1 matt subs r2, r2, #1 /* subtract one from count */ 136 1.3 matt strbeq r2, [r0], #1 /* zero? write trailing NUL */ 137 1.1 matt beq .Ldst_full /* zero? the dst has no more room */ 138 1.1 matt #endif 139 1.1 matt b .Ldst_align /* loop around for next byte */ 140 1.1 matt .Ldst_aligned: 141 1.1 matt tst r1, #3 /* get the misalignment of src */ 142 1.1 matt bne .Lincongruent /* !=? incongruent (slower) */ 143 1.1 matt 144 1.1 matt /* =? congruent (faster) */ 145 1.1 matt 146 1.1 matt .Lcongruent: 147 1.1 matt #if defined(STRLCPY) 148 1.1 matt add r6, r6, #3 /* compensate for word post-inc */ 149 1.1 matt #endif 150 1.1 matt b .Lcongruent_mainloop_load 151 1.1 matt .Lcongruent_mainloop: 152 1.1 matt #if defined(STRLCPY) || defined(STRNCPY) 153 1.1 matt subs r2, r2, #4 /* subtract 4 from the count */ 154 1.1 matt bmi .Lno_more_room 155 1.1 matt #endif 156 1.1 matt str r5, [r0], #4 /* store word into dst */ 157 1.1 matt #if defined(STRLCPY) 158 1.1 matt beq .Lno_more_room /* count is 0? no room in dst */ 159 1.1 matt #endif 160 1.1 matt #if defined(STRNCPY) 161 1.1 matt beq .Ldst_full_word_aligned /* count is 0? no room in dst */ 162 1.1 matt #endif 163 1.1 matt .Lcongruent_mainloop_load: 164 1.1 matt ldr r5, [r1], #4 /* load word from source */ 165 1.1 matt #if defined(_ARM_ARCH_6) 166 1.1 matt uqadd8 r3, r5, r7 /* magic happens here */ 167 1.1 matt mvns r3, r3 /* is the complemented result 0? */ 168 1.1 matt beq .Lcongruent_mainloop /* yes, no NULs, do it again */ 169 1.1 matt #else 170 1.1 matt tst r5, #BYTE0 /* does byte 0 contain a NUL? */ 171 1.1 matt tstne r5, #BYTE1 /* no, does byte 1 contain a NUL? */ 172 1.1 matt tstne r5, #BYTE2 /* no, does byte 2 contain a NUL? */ 173 1.1 matt tstne r5, #BYTE3 /* no, does byte 3 contain a NUL? */ 174 1.1 matt bne .Lcongruent_mainloop /* yes, no NULs, do it again */ 175 1.1 matt #endif 176 1.1 matt #if defined(STRLCPY) && 0 177 1.1 matt sub r1, r1, #3 /* back up src pointer */ 178 1.1 matt #endif 179 1.1 matt #if defined(_ARM_ARCH_6) 180 1.1 matt #ifdef __ARMEL__ 181 1.1 matt rev r3, r3 /* CLZ needs BE data */ 182 1.1 matt #endif 183 1.1 matt clz r3, r3 /* count leading zeros */ 184 1.1 matt #else 185 1.1 matt mov r3, #0 /* assume NUL is in byte 0 */ 186 1.1 matt tst r5, #BYTE0 /* is NUL in byte 2? */ 187 1.1 matt beq .Lcongruent_last_bytes /* yes, done searching. */ 188 1.1 matt mov r3, #8 /* assume NUL is in byte 1 */ 189 1.1 matt tst r5, #BYTE1 /* is NUL in byte 2? */ 190 1.1 matt beq .Lcongruent_last_bytes /* yes, done searching. */ 191 1.1 matt mov r3, #16 /* assume NUL is in byte 2 */ 192 1.1 matt tst r5, #BYTE2 /* is NUL in byte 2? */ 193 1.1 matt #if !defined(STRLCPY) 194 1.1 matt beq .Lcongruent_last_bytes /* yes, done searching. */ 195 1.1 matt mov r3, #24 /* NUL must be in byte 3 */ 196 1.1 matt #else 197 1.1 matt movne r3, #24 /* no, then NUL is in byte 3 */ 198 1.1 matt #endif 199 1.1 matt #endif /* _ARM_ARCH_6 */ 200 1.1 matt #if defined(STRLCPY) 201 1.1 matt .Lcongruent_last_bytes: 202 1.1 matt #endif 203 1.1 matt #if defined(STRLCPY) 204 1.1 matt add r1, r1, r3, lsr #3 /* position to point at NUL + 4 */ 205 1.1 matt #endif 206 1.1 matt b .Llast_bytes /* store the last bytes */ 207 1.1 matt 208 1.1 matt 209 1.1 matt .Lincongruent: 210 1.1 matt /* 211 1.1 matt * At this point dst is word aligned by src is not. Read bytes 212 1.1 matt * from src until it is read aligned. 213 1.1 matt */ 214 1.1 matt and r3, r1, #3 /* extract misalignment */ 215 1.1 matt mov r9, r3, lsl #3 /* calculate discard shift */ 216 1.1 matt rsb r8, r9, #32 /* calculate insertion shift */ 217 1.1 matt #if defined(STRLCPY) 218 1.1 matt add r6, r6, #3 /* compensate for word post-inc */ 219 1.1 matt #endif 220 1.1 matt bic r1, r1, #3 /* word align src */ 221 1.1 matt ldr r5, [r1], #4 /* load word frm src */ 222 1.1 matt mov r4, r5, lslo r9 /* discard lo bytes from src */ 223 1.1 matt tst r4, #BYTE0 /* does byte 0 contain a NUL? */ 224 1.1 matt #if defined(STRNCPY) 225 1.1 matt beq .Lend_of_string /* yes, zero fill rest of string */ 226 1.1 matt #else 227 1.1 matt moveq r3, r9 /* yes, set offset */ 228 1.1 matt beq .Lincongruent_end_of_string /* yes, deal with the last bytes */ 229 1.1 matt #endif 230 1.1 matt /* 231 1.1 matt * To make our test for NULs below do not generate false positives, 232 1.1 matt * fill the bytes in the word we don't want to match with all 1s. 233 1.1 matt */ 234 1.1 matt mvn r3, #0 /* create a mask */ 235 1.2 matt mov r3, r3, lslo r8 /* zero out bytes being kept */ 236 1.2 matt orr r5, r5, r3 /* merge src and mask */ 237 1.1 matt #ifdef _ARM_ARCH_6 238 1.2 matt uqadd8 r3, r5, r7 /* NUL detection magic happens */ 239 1.1 matt mvns r3, r3 /* is the complemented result 0? */ 240 1.1 matt beq .Lincongruent_mainloop_load /* yes, no NUL encountered! */ 241 1.1 matt #ifdef __ARMEL__ 242 1.1 matt rev r3, r3 /* CLZ wants BE input */ 243 1.1 matt #endif 244 1.1 matt clz r3, r3 /* count leading zeros */ 245 1.1 matt #else 246 1.1 matt /* 247 1.1 matt * We already tested for byte 0 above so we don't need to it again. 248 1.1 matt */ 249 1.1 matt mov r3, #24 /* assume NUL is in byte 3 */ 250 1.1 matt tst r5, #BYTE1 /* did we find a NUL in byte 1? */ 251 1.7 andvar subeq r3, r3, #8 /* yes, decrement byte position */ 252 1.1 matt tstne r5, #BYTE2 /* no, did we find a NUL in byte 2? */ 253 1.7 andvar subeq r3, r3, #8 /* yes, decrement byte position */ 254 1.1 matt tstne r5, #BYTE3 /* no, did we find a NUL in byte 3? */ 255 1.1 matt bne .Lincongruent_mainloop_load /* no, no NUL encountered! */ 256 1.1 matt #endif 257 1.1 matt mov r5, r4 /* discard already dealt with bytes */ 258 1.1 matt .Lincongruent_end_of_string: 259 1.1 matt #if defined(STRLCPY) 260 1.1 matt add r1, r1, r3, lsr #3 /* then add offset to NUL */ 261 1.1 matt #endif 262 1.1 matt sub r3, r3, r9 /* adjust NUL offset */ 263 1.1 matt b .Llast_bytes /* NUL encountered! finish up */ 264 1.1 matt 265 1.1 matt #if defined(STRLCPY) || defined(STRNCPY) 266 1.1 matt .Lincongruent_no_more_room: 267 1.1 matt mov r5, r4 /* move data to be stored to r5 */ 268 1.1 matt b .Lno_more_room /* fill remaining space */ 269 1.1 matt #endif /* STRLCPY || STRNCPY */ 270 1.1 matt 271 1.1 matt /* 272 1.1 matt * At this point both dst and src are word aligned and r4 contains 273 1.1 matt * partial contents from src. 274 1.1 matt */ 275 1.1 matt .Lincongruent_mainloop: 276 1.1 matt orr r4, r4, r5, lshi r8 /* put new src data into dst word */ 277 1.1 matt #if defined(STRLCPY) || defined(STRNCPY) 278 1.1 matt subs r2, r2, #4 /* subtract 4 from count */ 279 1.1 matt bmi .Lincongruent_no_more_room /* count < 0? dst will be full */ 280 1.1 matt #endif 281 1.1 matt str r4, [r0], #4 /* store word in dst */ 282 1.1 matt #if defined(STRLCPY) 283 1.1 matt beq .Lno_more_room /* space left is 0? stop copy */ 284 1.1 matt #endif 285 1.1 matt #if defined(STRNCPY) 286 1.1 matt beq .Ldst_full_word_aligned /* space left is 0? stop copy */ 287 1.1 matt #endif 288 1.1 matt mov r4, r5, lslo r9 /* move rest of src into dst word */ 289 1.1 matt .Lincongruent_mainloop_load: 290 1.1 matt ldr r5, [r1], #4 /* read src */ 291 1.1 matt #ifdef _ARM_ARCH_6 292 1.1 matt uqadd8 r3, r5, r7 /* magic happens here */ 293 1.1 matt mvns r3, r3 /* is the complemented result 0? */ 294 1.1 matt beq .Lincongruent_mainloop /* yes, no NUL encountered! */ 295 1.1 matt /* 296 1.1 matt * fall into this since we encountered a NULL. At this point we have 297 1.1 matt * from 1-5 bytes (excluding trailing NUL) to write. 298 1.1 matt */ 299 1.1 matt #ifdef __ARMEL__ 300 1.1 matt rev r3, r3 /* CLZ works on BE data */ 301 1.1 matt #endif 302 1.1 matt clz r3, r3 /* count leading zeroes */ 303 1.1 matt #else 304 1.1 matt tst r5, #BYTE0 /* does byte 0 contain a NUL? */ 305 1.1 matt tstne r5, #BYTE1 /* no, does byte 1 contain a NUL? */ 306 1.1 matt tstne r5, #BYTE2 /* no, does byte 2 contain a NUL? */ 307 1.1 matt tstne r5, #BYTE3 /* no, does byte 3 contain a NUL? */ 308 1.1 matt bne .Lincongruent_mainloop /* no, no NUL encountered! */ 309 1.1 matt /* 310 1.1 matt * fall into this since we encountered a NULL. At this point we have 311 1.1 matt * from 1-5 bytes (excluding trailing NUL) to write. 312 1.1 matt */ 313 1.1 matt mov r3, #0 /* assume a NUL is in byte 0 */ 314 1.1 matt tst r5, #BYTE0 /* is there a NUL in byte 0? */ 315 1.1 matt beq 1f /* yes, found a NUL! */ 316 1.1 matt mov r3, #8 /* assume a NUL is in byte 1 */ 317 1.1 matt tst r5, #BYTE1 /* is there a NUL in byte 0? */ 318 1.1 matt beq 1f /* yes, found a NUL! */ 319 1.1 matt tst r5, #BYTE2 /* is there a NUL in byte 2? */ 320 1.1 matt moveq r3, #16 /* yes, mark its position */ 321 1.1 matt movne r3, #24 /* no, it must be in byte 3 */ 322 1.1 matt 1: 323 1.1 matt #endif 324 1.1 matt orr r4, r4, r5, lshi r8 /* merge new and old src words */ 325 1.1 matt #if defined(STRLCPY) 326 1.1 matt add r1, r1, r3, lsr #3 /* adjust src to point to NUL */ 327 1.1 matt #endif 328 1.1 matt add r3, r3, r8 /* add remainder bytes worth */ 329 1.1 matt cmp r3, #32 /* do we have at least one word to write? */ 330 1.1 matt movlt r5, r4 /* no, move source bytes to expected reg */ 331 1.1 matt blt .Llast_bytes /* no, deal with them */ 332 1.1 matt #if defined(STRLCPY) 333 1.1 matt subs r2, r2, #4 /* subtract 4 from count */ 334 1.1 matt bpl 1f /* we have space for at least 4 */ 335 1.1 matt /* 336 1.1 matt * Since the space just went minus, we don't have enough room to 337 1.1 matt * write all 4 bytes. In fact, the most we can write is 3 so just 338 1.1 matt * just lie and say we have 3 bytes to write and discard the rest. 339 1.1 matt */ 340 1.1 matt add r2, r2, #4 /* add 4 back */ 341 1.1 matt mov r3, #24 /* say we have 3 bytes */ 342 1.1 matt mov r5, r4 /* discard the bytes we can't store */ 343 1.1 matt b .Llast_bytes /* and treat this as our last word */ 344 1.1 matt 1: 345 1.1 matt #elif defined(STRNCPY) 346 1.1 matt subs r2, r2, #4 /* subtract 4 from count */ 347 1.1 matt bmi .Lincongruent_no_more_room /* count < 0? dst will be full */ 348 1.1 matt #endif 349 1.1 matt str r4, [r0], #4 /* store dst word */ 350 1.1 matt #if defined(STRNCPY) 351 1.1 matt beq .Ldst_full_word_aligned /* space left is 0? stop copy */ 352 1.1 matt #endif 353 1.1 matt #if defined(STRLCPY) 354 1.1 matt bne 1f /* we still have space remaining */ 355 1.1 matt strb r2, [r0] /* write final NUL */ 356 1.1 matt b .Lend_of_string /* we are done */ 357 1.1 matt 1: 358 1.1 matt #endif 359 1.1 matt /* 360 1.1 matt * Subtract the 32 bits just written from the number of bits left 361 1.1 matt * to write. If 0 bits are left and not doing strncpy, just write 362 1.1 matt * the trailing NUL and be done. 363 1.1 matt */ 364 1.1 matt subs r3, r3, #32 /* we wrote one word */ 365 1.1 matt #if !defined(STRNCPY) 366 1.1 matt bne 1f /* no more data? */ 367 1.1 matt strb r3, [r0] /* write final NUL */ 368 1.1 matt b .Lend_of_string /* we are done */ 369 1.1 matt 1: 370 1.1 matt #endif 371 1.1 matt /* 372 1.1 matt * At this point after writing 4 bytes, we have 0 or 1 bytes left to 373 1.1 matt * write (excluding the trailing NUL). 374 1.1 matt */ 375 1.1 matt mov r5, r5, lslo r9 /* get remainder of src */ 376 1.1 matt 377 1.1 matt /* fall into .Llast_bytes */ 378 1.1 matt 379 1.1 matt #if !defined(STRLCPY) 380 1.1 matt .Lcongruent_last_bytes: 381 1.1 matt #endif 382 1.1 matt .Llast_bytes: 383 1.1 matt /* 384 1.1 matt * r5 contains the last word and is in host byte order. 385 1.1 matt * r3 contains number of bits left to copy (0..31). 386 1.1 matt * r1 should point to the NUL + 4. 387 1.1 matt */ 388 1.1 matt bics ip, r3, #7 /* truncate bits, is result 0? */ 389 1.1 matt #if !defined(STRNCPY) 390 1.1 matt bne 1f /* no, have to write some bytes */ 391 1.1 matt strb ip, [r0] /* yes, write trailing NUL */ 392 1.1 matt b .Lend_of_string /* yes, and we are the end */ 393 1.1 matt 1: 394 1.1 matt #endif 395 1.1 matt #if defined(STRLCPY) || defined(STRNCPY) 396 1.1 matt cmp r2, ip, lsr #3 /* is there enough room? */ 397 1.1 matt movlt ip, r2, lsl #3 /* no, only fill remaining space */ 398 1.1 matt #endif 399 1.1 matt mvn r3, #0 /* create a mask */ 400 1.1 matt mov r3, r3, lshi ip /* clear leading bytes */ 401 1.1 matt bic r5, r5, r3 /* clear trailing bytes */ 402 1.1 matt #if defined(STRNCPY) 403 1.1 matt cmp r2, #4 /* room for 4 bytes? */ 404 1.1 matt movge ip, #32 /* yes, we will write 4 bytes */ 405 1.1 matt bge 2f /* yes, and go do it */ 406 1.1 matt mvn r3, #0 /* create a mask (again) */ 407 1.1 matt mov ip, r2, lsl #3 /* remaining space bytes -> bits */ 408 1.1 matt mov r3, r3, lshi ip /* clear remaining bytes */ 409 1.1 matt #elif defined(STRLCPY) 410 1.1 matt cmp r2, #3 /* do we have room for 3 bytes & NUL? */ 411 1.1 matt bge 2f /* yes, just clear out dst */ 412 1.1 matt mov r3, r3, lshi #8 /* mask out trailing NUL */ 413 1.1 matt #else 414 1.1 matt cmp ip, #24 /* are we writing 3 bytes & a NUL? */ 415 1.1 matt bge 2f /* yes, just overwrite dst */ 416 1.1 matt mov r3, r3, lshi #8 /* mask out trailing NUL */ 417 1.1 matt #endif /* !STRNCPY */ 418 1.1 matt ldr r4, [r0] /* fetch dst word */ 419 1.1 matt and r4, r4, r3 /* preserve trailing bytes */ 420 1.1 matt orr r5, r5, r4 /* merge dst with src */ 421 1.1 matt 2: str r5, [r0], #4 /* store last word */ 422 1.1 matt #if defined(STRNCPY) 423 1.1 matt subs r2, r2, ip, lsr #3 /* subtract bytes cleared from count */ 424 1.1 matt beq .Ldst_full_word_aligned 425 1.1 matt #endif 426 1.1 matt b .Lend_of_string 427 1.1 matt 428 1.1 matt #if defined(STRLCPY) || defined(STRNCPY) 429 1.1 matt .Lno_more_room: 430 1.1 matt #if defined(STRLCPY) 431 1.1 matt cmp r2, #-1 /* tried to write 3 bytes? */ 432 1.1 matt blt 1f /* less, partial word write */ 433 1.1 matt cmp r2, #0 /* no space left? */ 434 1.3 matt strbeq r2, [r0] /* write the final NUL */ 435 1.1 matt bicne r5, r5, #BYTE3 /* clear trailing NUL */ 436 1.1 matt strne r5, [r0] /* write last word */ 437 1.1 matt b .Ldst_full_word_aligned /* the dst buffer is full */ 438 1.1 matt 1: 439 1.1 matt #endif /* STRLCPY */ 440 1.1 matt add r2, r2, #4 /* restore remaining space */ 441 1.1 matt ldr r4, [r0] /* load dst */ 442 1.1 matt mvn r3, #0 /* create a mask */ 443 1.1 matt mov r2, r2, lsl #3 /* bytes -> bits */ 444 1.1 matt mov r3, r3, lshi r2 /* clear leading bytes */ 445 1.1 matt bic r5, r5, r3 /* clear trailing bytes from src */ 446 1.1 matt #if defined(STRLCPY) 447 1.1 matt mov r3, r3, lshi #8 /* mask out trailing NUL */ 448 1.1 matt #endif /* STRLCPY */ 449 1.1 matt and r4, r4, r3 /* preserve trailing bytes in dst */ 450 1.1 matt orr r4, r4, r5 /* merge src with dst */ 451 1.1 matt str r4, [r0], #4 /* write last word */ 452 1.1 matt b .Ldst_full_word_aligned 453 1.1 matt #endif /* STRLCPY || STRNCPY */ 454 1.1 matt 455 1.1 matt #if defined(STRLCPY) 456 1.1 matt /* 457 1.1 matt * Destination was filled (and NUL terminated). 458 1.1 matt * All that's left is count the number of bytes left in src. 459 1.1 matt */ 460 1.1 matt .Ldst_full: 461 1.1 matt 1: tst r1, #3 /* dst word aligned? */ 462 1.1 matt beq 2f /* yes, so do it word by word */ 463 1.1 matt ldrb r5, [r1], #1 /* load next byte */ 464 1.1 matt teq r5, #0 /* is it a NUL? */ 465 1.1 matt bne 1b /* no, check alignment */ 466 1.1 matt b .Lend_of_string /* and return */ 467 1.1 matt 2: add r6, r6, #3 /* compensate for post-inc */ 468 1.1 matt .Ldst_full_word_aligned: 469 1.1 matt 3: ldr r5, [r1], #4 /* load word from src */ 470 1.1 matt #ifdef _ARM_ARCH_6 471 1.1 matt uqadd8 r5, r5, r7 /* perform NUL magic */ 472 1.1 matt mvns r5, r5 /* complement all 0s? */ 473 1.1 matt beq 3b /* yes, no NUL so get next word */ 474 1.1 matt #else 475 1.1 matt tst r5, #BYTE0 /* does byte 0 contain a NUL? */ 476 1.1 matt tstne r5, #BYTE1 /* no, does byte 1 contain a NUL? */ 477 1.1 matt tstne r5, #BYTE2 /* no, does byte 2 contain a NUL? */ 478 1.1 matt tstne r5, #BYTE3 /* no, does byte 3 contain a NUL? */ 479 1.1 matt bne 3b /* no, no NUL encountered! */ 480 1.1 matt #endif 481 1.1 matt #ifdef _ARM_ARCH_6 482 1.1 matt #ifdef __ARMEL__ 483 1.1 matt rev r5, r5 /* CLZ needs BE data */ 484 1.1 matt #endif 485 1.1 matt clz r5, r5 /* count leading zeros */ 486 1.1 matt add r1, r1, r5, lsr #3 /* add offset to NUL to src pointer */ 487 1.1 matt #else 488 1.1 matt tst r5, #BYTE0 /* is there a NUL in byte 0? */ 489 1.1 matt beq 4f /* yes, don't check any further */ 490 1.1 matt add r1, r1, #1 /* no, advance src pointer by 1 */ 491 1.1 matt tst r5, #BYTE1 /* is there a NUL in byte 1? */ 492 1.1 matt beq 4f /* yes, don't check any further */ 493 1.1 matt add r1, r1, #1 /* no, advance src pointer by 1 */ 494 1.1 matt tst r5, #BYTE2 /* is there a NUL in byte 2? */ 495 1.1 matt addne r1, r1, #1 /* no, there must be in byte 3 */ 496 1.1 matt 4: 497 1.1 matt #endif /* _ARM_ARCH_6 */ 498 1.1 matt .Lend_of_string: 499 1.1 matt sub r0, r1, r6 /* subtract start from finish */ 500 1.1 matt pop {r4-r9} /* restore registers */ 501 1.1 matt RET 502 1.1 matt #elif defined(STRNCPY) 503 1.1 matt .Lend_of_string: 504 1.1 matt teq r2, #0 /* any bytes left to zero? */ 505 1.1 matt beq 3f /* no, just return. */ 506 1.1 matt mov r1, #0 /* yes, prepare to zero */ 507 1.1 matt cmp r2, #16 /* some, but not a lot? */ 508 1.1 matt ble 1f 509 1.1 matt mov r4, lr /* preserve lr */ 510 1.1 matt bl PLT_SYM(_C_LABEL(memset)) /* yes, and let memset do it */ 511 1.1 matt mov lr, r4 /* restore lr */ 512 1.1 matt b 3f /* return */ 513 1.1 matt 1: add ip, r0, r2 /* calculate stopping point */ 514 1.1 matt 2: strb r1, [r0], #1 /* clear a byte */ 515 1.1 matt cmp r0, ip /* done? */ 516 1.1 matt blt 2b /* no, clear next byte */ 517 1.1 matt 3: mov r0, r6 /* restore dst pointer */ 518 1.1 matt pop {r4-r9} /* restore registers */ 519 1.1 matt RET 520 1.1 matt .Ldst_full: 521 1.1 matt .Ldst_full_word_aligned: 522 1.1 matt /* 523 1.1 matt * Destination was filled (but not NUL terminated). 524 1.1 matt * All that's left is return the start of dst 525 1.1 matt */ 526 1.1 matt mov r0, r6 /* restore dst pointer */ 527 1.1 matt pop {r4-r9} /* restore registers */ 528 1.1 matt RET 529 1.1 matt #else 530 1.1 matt .Lend_of_string: 531 1.1 matt mov r0, r6 /* restore dst pointer */ 532 1.1 matt pop {r4-r9} /* restore registers */ 533 1.1 matt RET 534 1.1 matt #endif 535 1.1 matt END(FUNCNAME) 536