串操作指令(String Instructions)

串指令对内存块进行批量操作,结合 rep 前缀自动循环,使用隐式寄存器(i386 约定):

  • 源地址:%rsi(32 位 %esi
  • 目的地址:%rdi(32 位 %edi
  • 计数:%rcx(32 位 %ecx
  • 方向标志 DF:cld(清 0,地址递增)/std(置 1,地址递减)

每条串指令处理一个"元素",元素大小由后缀决定:b(1) w(2) l(4) q(8)。执行后 rsi/rdi 按方向 ± 元素大小移动,rcx 减 1。

基本串指令

指令 作用 等价语义
movs 复制 ES:[rdi] = DS:[rsi]
cmps 比较 cmp [rsi],[rdi]
scas 扫描 cmp al/ax/eax/rax, [rdi]
stos 存储 [rdi] = acc
lods 加载 acc = [rsi]
ins / outs 端口 I/O 需特权(IOPL),用户态一般不可用

rep 前缀家族

前缀 语义
rep rcx!=0 时重复(用于 movs/stos
repne / repnz rcx!=0 且 ZF=0 时重复(用于 cmps/scas
repe / repz rcx!=0 且 ZF=1 时重复

注意:rep 是"先判断后执行",rcx=0 时一次也不执行——不需要在循环前单独判断 0 长度。

示例:内存复制(memcpy)

# memcpy(rdi=dst, rsi=src, rdx=n) —— n 为字节数
memcpy:
    movq %rdx, %rcx
    cld                     # 地址递增
    rep movsb               # 逐字节复制 rcx 次
    ret

若按 8 字节复制更快(需 n 为 8 的倍数且对齐):

memcpy8:
    movq %rdx, %rcx
    shrq $3, %rcx           # 字节数 -> 八字节数
    cld
    rep movsq               # 主块按 8 字节复制
    movq %rdx, %rcx
    andq $7, %rcx           # 余数部分
    rep movsb               # 按字节补齐
    ret

示例:内存填充(memset)

# memset(rdi=dst, rsi=value, rdx=n)
memset:
    movq %rdx, %rcx
    movb %sil, %al
    cld
    rep stosb
    ret

示例:查找字节(memchr / 扫描)

# 在缓冲区中查找目标字节,返回地址或 NULL
# rdi=buf, rcx=len, al=target
find_byte:
    cld
    repne scasb             # 逐字节比较,找到则 ZF=1
    je    found
    xorq  %rax, %rax        # 未找到返回 0
    ret
found:
    leaq -1(%rdi), %rax     # rdi 已越过匹配处,回退 1
    ret

注意事项

  • rep movs/rep stos 由 CPU 内部做微码优化,对大块通常比手写循环快;超大块还可配合 erms(Enhanced REP MOVS)特性。小块数据用 SIMD 或普通循环可能更优。
  • 不要对重叠区域用 rep movsb 正向复制(应用反向 std+rep movsbmemmove 逻辑)。
  • lodsrep 配合较少见,因其不断覆盖累加器、只留下最后一个元素。
  • ABI 要求:System V 规定函数入口/出口处 DF 必须为 0——若函数内用了 std,返回前务必 cld,否则会破坏调用者(如 C 代码)的行为。