前言
同样是一个 很常用的 glibc 库函数
不管是 用户业务代码 还是 很多类库的代码, 基本上都会用到 字符串长度的计算
不过 我们这里是从 具体的实现 来看一下
它的实现 主要是使用 汇编 来进行实现的, 因此 理解需要一定的基础
测试用例
就是简单的使用了一下 strcpy, strlen
root@ubuntu:~/Desktop/linux/HelloWorld# cat Test03Strcpy.c
#include "stdio.h"
int main(int argc, char** argv) {
int x = 2;
int y = 3;
int z = x + y;
char* p1 = (char*) malloc(20);
char* p2 = (char*) malloc(20);
printf("p1 : 0x%x\n", p1);
printf("p2 : 0x%x\n", p2);
memset(p1, 'a', 12);
strcpy(p2, p1);
int p2Len = strlen(p2);
printf(" p1 = %s, p2 = %s, p2Len = %d\n ", p1, p2, p2Len);
}
strlen 的实现
先试判断 前面16字节, 然后再处理后面 48 字节
取开始的十六个字节到 xmm4, 然后和 0 做 pcmpeqb [如果某一个字节为 0, 则将该字节置为 0xff, 否则置为 0], 值保存在 xmm4
将 xmm4 末尾的十六字节的数据转换到 rdx
如果 edx 为 0, 则表示 前16字节均不为 0, 跳到处理 接下来的 48 字节的流程
否则 通过 bsf 指令获取 edx 第一个不为 0 的索引, 给 ax 返回
如果前面 16 字节均不为 0
则接下来处理接下来的的 48 字节, 道理类似, 讲接下来的 48 字节以 16字节 为单位 和 0 进行比较, 比较结果分别 存储在 dx, r8, cx, 然后 将最终结果 merge 到 rdx
如果 接下来的 48 字节均不为 0, 跳转到后面循环处理
否则 通过 bsfq 指令获取 edx 第一个不为 0 的索引, 给 ax 返回
PCMPEQB/PCMPEQW/PCMPEQD — Compare Packed Data for Equal
PMOVMSKB — Move Byte Mask
对于 前面 64 字节均不为 0 的场景, 接下来的处理如下
以 64 字节为单位来进行比较, 然后 找到存在 0 的一组
然后 计算当前偏移 减去原始地址, 即为当前字符串的长度
PMINUB/PMINUW — Minimum of Packed Unsigned Integers
strlen 实例
上面的 case, 调用 strlen 的地方
其中 rdi 为传入的 字符串的地址
rdx 为 前面十六字节 和 0 做 pcmpeqb 操作之后的结果
通过 bsf 指令获取 rdx 中第一个为 1 的索引, 存储在 ax 中, 为 12
完