1/* 2 * Copyright (c) 2011 Apple, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29#include <arm/arch.h> 30.syntax unified 31.code 32 32.globl _strnlen 33 34#define addr r0 35#define maxl r1 36#define temp r2 37#define mask r3 38#define save ip 39#define word lr 40#define byte lr 41#define indx r0 42 43.macro IfHS_and_WordDoesntContainNUL_SetZ 44#if defined _ARM_ARCH_6 45// In each word of the string, we check for NUL bytes via a saturating 46// unsigned subtraction of each byte from 0x1. The result of this is 47// non-zero if and only if the corresponding byte in the string is NUL. 48// Simply using a TST instruction checks all four bytes for NULs in one 49// go. 50 uqsub8 temp, mask, word 51 tsths temp, temp 52#else 53// If we're on armv5, we do not have the uqsub8 instruction, so we need 54// to use a different test for NUL. Instead, we compute: 55// 56// byte - 0x1 & ~byte 57// 58// and test the high-order bit. If it is set, then byte is NUL. Just 59// as with the other test, this can be applied simultaneously to all 60// bytes in a word. 61 sub temp, word, mask 62 bic temp, temp, word 63 tsths temp, mask, lsl #7 64#endif 65.endm 66 67.text 68.align 3 69.long 0x0 // padding 70.long 0x01010101 // mask for use in finding NULs 71_strnlen: 72// Establish stack frame, load mask that we will use to find NUL bytes, 73// and set aside a copy of the pointer to the string. Subtract 4 from 74// the maxlen, and jump into a byte-by-byte search if this requires a 75// borrow, as we cannot use a word-by-word search in that case. 76 push {r7,lr} 77 mov r7, sp 78 ldr mask, (_strnlen-4) 79 add save, addr, #4 80 subs maxl, maxl, #4 81 blo L_bytewiseSearch 82 83// Load the aligned word that contains the start of the string, then OR 84// 0x01 into any bytes that preceed the start to prevent false positives 85// when we check for NUL bytes. Additionally, add the number of unused 86// bytes to maxlen. 87 and temp, addr, #3 88 bic addr, addr, #3 89 add maxl, maxl, temp 90 lsl temp, temp, #3 91 ldr word, [addr], #4 92 rsb temp, temp, #32 93 orr word, word, mask, lsr temp 94 95 subs maxl, maxl, #4 96 IfHS_and_WordDoesntContainNUL_SetZ 97 bne 1f 98 99.align 4 1000: ldr word, [addr], #4 101 subs maxl, maxl, #4 102 IfHS_and_WordDoesntContainNUL_SetZ 103 beq 0b 104 105.align 4 106// Either the last word that we loaded contained a NUL, or we will 107// exceed maxlen before we finish the next word in the string. Determine 108// which case we are in by repeating the check for NUL, and branch if 109// there was not a NUL byte. Padding ensures that we don't have two 110// branches in a single 16-byte fetch group, as this interferes with 111// branch prediction on Swift. 1121: tst temp, temp 113 beq L_bytewiseSearch 114 115// The last word that we loaded contained a NUL. Subtracting the saved 116// pointer from the current pointer gives us the number of bytes from 117// the start of the string to the word containing the NUL. 118 sub indx, addr, save 119#if defined _ARM_ARCH_6 120// To that we add the index of the first NUL byte in the word, computed 121// using REV and CLZ followed by a shift. 122 rev temp, temp 123 clz temp, temp 124 add indx, indx, temp, lsr #3 125#else 126// armv5 does not have the REV instruction, so instead we find the 127// index of the NUL byte in word with a linear search. 128 tst word, #0x000000ff 129 addne indx, #1 130 tstne word, #0x0000ff00 131 addne indx, #1 132 tstne word, #0x00ff0000 133 addne indx, #1 134#endif 135 pop {r7,pc} 136 137.align 4 138L_bytewiseSearch: 139// Restore maxlen (the last thing that happened before we branched here 140// was that we subtracted 4 from maxlen), and adjust the saved string 141// pointer. Then we do a simple byte-by-byte search until we either 142// reach the end of the string or maxlen reaches zero, at which point 143// the length to return is simply the difference between the current 144// and saved pointers. 145 adds maxl, maxl, #4 146 sub save, save, #4 147 beq 1f 1480: ldrb byte, [addr] 149 cmp byte, #0 150 addhi addr, #1 151 subshi maxl, #1 152 bhi 0b 1531: sub indx, addr, save 154 pop {r7,pc} 155