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.