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