too late but I still provide my answer.
So let us make the difference between
main()
{
char *a="blabla";
a[3]='x';
}
and this one, yours.
main()
{
char a[7] = "blabla"
a[3]='x';
}
So there is a big difference between them.
In the first case the object a is a pointer whose value points to the beginning of the blabla string.
Dumping the assembled code, we see:
4004aa: 48 c7 45 f8 54 05 40 movq $0x400554,-0x8(%rbp)
4004b1: 00
4004b2: 48 8b 45 f8 mov -0x8(%rbp),%rax
4004b6: 48 83 c0 03 add $0x3,%rax
4004ba: c6 00 78 movb $0x78,(%rax)
So, it tries and set the pointer toward the address 0x400554.
Objdumpo reports that this address is in the .rodata segment.
Disassembly of section .rodata:
0000000000400550 <_IO_stdin_used>:
400550: 01 00 add %eax,(%rax)
400552: 02 00 add (%rax),%al
400554: 62 (bad)
400555: 6c insb (%dx),%es:(%rdi)
400556: 61 (bad)
400557: 62 .byte 0x62
400558: 6c insb (%dx),%es:(%rdi)
400559: 61 (bad)
So, the compiler installed the string blabla in .rodata at that address and after that it tries to modify the .rodata segment, finishing with segmentation fault.
readelf reports no W access on .rodata:
[13] .rodata PROGBITS 0000000000400550 00000550
000000000000000b 0000000000000000 A 0 0 4
On the other hand, what you try to do (the 2nd program) is compiled so:
00000000004004a6 <main>:
4004a6: 55 push %rbp
4004a7: 48 89 e5 mov %rsp,%rbp
4004aa: c7 45 f0 62 6c 61 62 movl $0x62616c62,-0x10(%rbp)
4004b1: 66 c7 45 f4 6c 61 movw $0x616c,-0xc(%rbp)
4004b7: c6 45 f6 00 movb $0x0,-0xa(%rbp)
4004bb: c6 45 f3 78 movb $0x78,-0xd(%rbp)
In this case, the array object a is allocated 7 bytes on the stack frame, starting from offset %RBP-0xA up to %RBP-0x10.
When it tries to do a[3]='x' it will modify the stack at %RBP-0xD. The stack has write permission, all is all right.
For more information I suggest you to read https://en.wikipedia.org/wiki/Identity_and_change