x86汇编入门:数组索引使用异常触发code 10、signal 11报错的咨询
汇编代码两类报错的根因与修正方案
两类报错的本质区别
signal 11(段错误SIGSEGV):程序运行时访问了操作系统未授权的内存地址,触发内存访问违规,属于运行阶段才会出现的问题。code 10类编译报错:代码的寻址逻辑不符合x86指令集的语法规则,链接器无法计算出合法内存地址,在编译阶段就会被拦截,程序根本无法启动。
原代码触发问题的具体原因
- 非法栈跳转触发段错误
程序被操作系统直接加载到内存执行时,入口点_start是被内核直接跳转进入的,栈上不存在普通函数调用的返回地址。你在.printthis块调用完打印系统调用后直接写了ret,会把栈顶存的命令行参数值当作返回地址跳转,直接访问到无权限的低地址空间,这是触发signal11的核心原因。
原本写在jmp .printthis之后的标准退出代码mov eax,1; int 0x80被无条件跳转直接跳过,根本没有执行到正确的程序退出逻辑。 - 数组访问的尺寸不匹配
你用db声明的数组每个元素仅占1字节,但你写mov eax, [array]是按32位尺寸从内存读值,会一次性连续读取从array起始地址开始的4个字节(也就是1、2、3、4四个相邻元素)拼成一个32位整数存入eax,完全和“取数组第一个元素的值为1”的预期不符。 - 非法寻址触发编译报错
你尝试写的mov eax, [array*4]会直接触发code10报错,原因是 x86指令集的寻址规则中,比例因子仅允许作用在寄存器上,不允许直接乘在数据标签上。合法的比例寻址格式只能是「基址寄存器 + 索引寄存器*比例因子(1/2/4/8) + 立即数偏移」,把array标签直接乘4属于完全不符合语法的写法,链接器无法计算对应地址,自然编译失败。
你之前写的mov ebx, [array+1]语法是合法的,只是因为按32位尺寸读单字节数组,会把相邻的内存字节都读入ebx,得到的是不符合预期的脏值。
可正常运行的修正代码
section .text global _start _start: ; 按字节尺寸读取单字节数组元素,避免读入相邻的脏值 mov al, [array] mov bl, [array+1] add al, bl ; 要输出可见数字的话,需要把数值转成对应ASCII编码 add al, 0x30 mov [sum], al .printthis: mov ecx, sum mov edx, 1 mov ebx, 1 mov eax, 4 int 0x80 ; 打印完成后走标准系统调用退出,不能使用ret mov eax, 1 int 0x80 section .data array db 1,2,3,4,5 section .bss sum resb 1
核心知识点梳理
- 32位x86 Linux的用户程序必须通过
int 0x80的exit系统调用主动退出,不能依赖普通函数的ret指令返回——因为程序入口没有被常规函数调用,栈上不存在合法的返回地址。 - 不同尺寸的内存读取指令要和数组声明匹配:
- 用
db声明的1字节数组,读取时要用mov al/bl/cl...这类8位寄存器存值 - 用
dw声明的2字节数组,读取时要用mov ax/bx/cx...这类16位寄存器存值 - 用
dd声明的4字节数组,读取时用32位寄存器即可
- 用
- 合法的索引访问写法:如果要访问数组里第i个4字节元素,需要先把索引值存到寄存器中,再写
mov eax, [array + esi*4],不能直接对array标签做乘法操作。 - 段错误的高频触发场景:无权限的内存读写、非法栈跳转、跳转到不可执行的内存区域。
内容的提问来源于stack exchange,提问作者Futman
相关产品推荐
相关产品推荐

