x86汇编文件写入段错误排查及硬编码文件名实现
问题分析与解决方案
段错误的原因及修复
你的汇编程序触发段错误主要有两个核心问题:
1. 栈帧初始化错误
_start并非普通C函数,程序启动时ebp未被调用者初始化。你的代码仅执行了pushl %ebp,但未将当前栈指针esp赋值给ebp,导致后续通过8(%ebp)访问argv[1]时,访问的是非法内存地址,直接触发段错误。
正确的栈帧初始化应该补充:
_start: pushl %ebp movl %esp, %ebp ; 将当前栈指针赋值给ebp,建立有效栈帧
或者直接利用_start固定的栈布局访问argv[1](启动时栈顶为argc,4(%esp)是argv[0],8(%esp)是argv[1]),无需手动建立栈帧。
2. Open系统调用的Flags参数错误
你定义的O_CREATE_WRITE为八进制03101,包含无效标志位,会导致open调用失败(返回-1),后续使用错误的文件描述符执行write也会引发异常。正确的创建并写入、截断已有文件的标志组合是O_WRONLY | O_CREAT | O_TRUNC,对应八进制0301(十进制193)。
修正后的完整代码
.section .data .equ SYS_OPEN, 5 .equ SYS_CLOSE, 6 .equ SYS_WRITE, 4 .equ SYS_READ, 3 .equ SYS_EXIT, 1 .equ LINUX_SYSCALL, 0x80 .equ STDIN, 0 .equ STDOUT, 1 .equ STDERR, 2 .equ O_CREATE_WRITE, 0301 ; 修正后的有效标志位 .equ TEXT_SIZE, 17 text: .ascii "hey diddle diddle" ; 无需末尾\0,write不需要写入终止符 .section .text .globl _start _start: pushl %ebp movl %esp, %ebp ; 建立正确栈帧 # 预留内存存储文件描述符 subl $4, %esp # 打开目标文件 movl $SYS_OPEN, %eax movl 8(%ebp), %ebx ; 此时ebp有效,8(%ebp)指向argv[1] movl $O_CREATE_WRITE, %ecx movl $0666, %edx int $LINUX_SYSCALL movl %eax, -4(%ebp) ; 保存文件描述符 _write_text: # 执行write系统调用 movl $SYS_WRITE, %eax movl -4(%ebp), %ebx ; 传入文件描述符 movl $text, %ecx ; 传入文本地址 movl $TEXT_SIZE, %edx; 传入文本长度 int $LINUX_SYSCALL _exit: # 关闭文件 movl $SYS_CLOSE, %eax movl -4(%ebp), %ebx int $LINUX_SYSCALL # 退出程序 movl $SYS_EXIT, %eax movl $0, %ebx int $LINUX_SYSCALL
硬编码文件名的实现方法
若无需通过argv传入文件名,只需在.data段定义文件名字符串(必须以\0结尾,符合C风格字符串要求),然后在open调用时直接使用该字符串地址即可:
.section .data .equ SYS_OPEN, 5 .equ SYS_CLOSE, 6 .equ SYS_WRITE, 4 .equ SYS_EXIT, 1 .equ LINUX_SYSCALL, 0x80 .equ O_CREATE_WRITE, 0301 .equ TEXT_SIZE, 17 filename: .ascii "hey.txt\0" ; 硬编码文件名,必须以\0结尾 text: .ascii "hey diddle diddle" .section .text .globl _start _start: pushl %ebp movl %esp, %ebp subl $4, %esp # 打开硬编码的目标文件 movl $SYS_OPEN, %eax movl $filename, %ebx ; 直接使用硬编码文件名的地址 movl $O_CREATE_WRITE, %ecx movl $0666, %edx int $LINUX_SYSCALL movl %eax, -4(%ebp) _write_text: movl $SYS_WRITE, %eax movl -4(%ebp), %ebx movl $text, %ecx movl $TEXT_SIZE, %edx int $LINUX_SYSCALL _exit: movl $SYS_CLOSE, %eax movl -4(%ebp), %ebx int $LINUX_SYSCALL movl $SYS_EXIT, %eax movl $0, %ebx int $LINUX_SYSCALL
编译链接步骤不变:
as --32 hey.s -o hey.o ld -m elf_i386 hey.o -o hey ./hey # 硬编码版本无需传入文件名参数
内容的提问来源于stack exchange,提问作者Awolez
相关产品推荐
相关产品推荐

