On Linux with the standard toolchain (GNU Binutils ld
), .text
is a "special" section name that gets special treatment (exec permission by default), but .code
isn't. (Other special sections include .data
(writeable) and .bss
(writable nobits), and all with a default alignment > 1.)
section .text
is the NASM ELF/Linux equivalent of Windows MASM .code
directive, but that does not mean that Linux tools recognize a .code
directive or section name1.
section .code
is no different from section xyz123
; it just uses the defaults which are noexec
nowrite
. See the other
entry at the bottom of the table in the NASM docs.
Use readelf -a hello
to see the section (linking) and segment (program-loader) attributes, with a distinct lack of an X
anywhere.
Footnote 1: In fact, I think Windows executables still use the actual section name .text
. At least GNU objdump -d
still says the code is in the .text
section.
So the MASM .code
directive is a shortcut for switching to the .text
section.
Fun fact: this does happen to run correctly "by accident" if you build it as 32-bit code (which you should because it's using only 32-bit int 0x80
system calls), like in this case that used section .code
when incorrectly porting from 16-bit MASM code to Linux NASM.
Or if you'd run your 64-bit code on an older kernel.
The reason is that without explicitly specifying a PT_GNU_STACK
note, the kernel uses backwards-compat assumptions for 32-bit executables and uses READ_IMPLIES_EXEC
which affects every single page: Linux default behavior of executable .data section changed between 5.4 and 5.9?. Older kernels do this even for 64-bit executables, newer kernels only make the stack itself executable in this case.
Adding section .note.GNU-stack noalloc noexec nowrite progbits
to your source makes it segfault as it should, even when build into a 32-bit executable. (nasm -felf32
/ ld -melf_i386 -o foo foo.o
). See this answer.
See also Unexpected exec permission from mmap when assembly files included in the project about the old situation.