使用x86 AT&T内联汇编修改C字符数组触发段错误的问题排查
Great question! Let's break down exactly why your code is hitting a segmentation fault, then walk through the fixes.
The Root Cause
Your core issue is a misunderstanding of how GCC inline assembly constraints work. When you used the m constraint for string, you told the compiler that %0 represents the memory contents of the array, not its address.
Let's look at the problematic line:
mov %0, %%eax;
Since %0 is tied to a m constraint, this instruction doesn't load the address of string into eax—it loads the first byte of the array (the character 'a', ASCII 0x61) into eax. Then when you try to write to 1(%%eax), you're attempting to write to memory address 0x62, which is a low, invalid address (typically part of the OS's protected memory space). That's why you get a segmentation fault.
Additional Issues in Your Original Code
- The
=m"(string)output constraint is unnecessary: you're modifying the array in-place, not assigning a new value to the entire array variable. - Using
"0"(string)to tie the input operand to the output operand is incorrect here, since=mmarks the operand as write-only—compilers may not handle this correctly for read operations.
Fixed Code Examples
Option 1: Let the Compiler Manage the Register (Best Practice)
This approach uses a r constraint to tell the compiler to pass the address of string in a register, then we directly write to the offset:
#include <stdio.h> int main() { char string[] = "ahoy"; __asm__ volatile ( "movb $'X', 1(%0);" // Write 'X' to the 2nd byte of the array : // No output operands needed : "r"(string) // Pass the address of string in %0 : "memory" // Tell compiler memory was modified ); printf("%s\n", string); return 0; }
Option 2: Explicitly Load the Address with lea
If you specifically want to use eax, you can use lea (load effective address) to fetch the array's address from the memory operand:
#include <stdio.h> int main() { char string[] = "ahoy"; __asm__ volatile ( "lea %0, %%eax;" // Load the address of string into eax "movb $'X', 1(%%eax);" // Write 'X' to offset 1 : : "m"(string) // Reference the array's memory : "memory", "eax" // Mark eax and memory as clobbered ); printf("%s\n", string); return 0; }
Key Takeaways for GCC Inline Assembly
- Use
rconstraints when you need to pass a pointer/address to assembly—this gives you the actual memory address in a register. - Use
mconstraints when you need to directly reference memory locations, but remember%0will represent the contents, not the address. - Include
"memory"in the clobber list whenever you modify memory directly—this prevents the compiler from optimizing away critical reads/writes to that memory. - Avoid manually specifying registers (like
eax) unless you have to—letting the compiler manage registers leads to more efficient, portable code.
内容的提问来源于stack exchange,提问作者John Doe

