Call _exit(0); or exit_group(0) at the end of _start.  (Link with gcc -static -nostartfiles instead of -nostdlib so you can call the libc system call wrapper functions; they should work even though glibc init functions haven't been run so malloc or printf would crash ).
Or make an exit_group(0) system-call manually with inline asm.  On x86-64 Linux:
asm("mov $231, %eax; xor %edi,%edi; syscall");
See also How Get arguments value using inline assembly in C without Glibc? for more about writing a hacky x86-64 _start to run your own C function as the first thing in your process.  (But most of that answer is about hacking the calling convention to access argc / argv, which is nasty and I don't recommend it.)  Matteo's answer on that question has a whole minimal _start written in asm that calls a normal C main function.
The book's code is just plain broken for 2 reasons.  (I don't know how it could have ever worked on i386 or x86-64.  Seems super weird to me.  Are you sure it wasn't just supposed to crash, but you look at what it does before that happens?)
- _startis not a function in Linux; you (or compiler-generated code) can't- retfrom it.  You need to make an- _exitsystem call.  There is no return address on the stack1.
 - Where a function would have its return address, the ELF entry point - _starthas- argc, as specified in the ABI docs.  (x86-64 System V or i386 System V depending on whether you build a 64-bit or- gcc -m3232-bit executable.)
 
- Inserting - leave(which does- mov %ebp, %esp/- pop %ebpor the RBP / RSP equivalent) into the compiler-generated code makes no sense here.  It's sort of like an extra- pop, but breaks the compiler's- EBP/- RBPso if it happens to choose- leaveinstead of- pop %rbpfor its own prologue then the compiler generated code will fault.  (RBP on entry to- _startis 0 in a  statically-linked executable.  Or holding whatever the dynamic linker left in RBP before jumping to- _startin a PIE executable.)
 - But ultimately, GCC will compile - _startas a normal function, thus eventually running a- retinstruction.  There is no valid / useful return address anywhere, so there's no way- retcan work at all.
 - If you compile without optimization (the default), gcc will default to - -fno-omit-frame-pointer, so its function prologue will have set up EBP or RBP as a frame pointer making it possible for- leaveitself to not fault.  If you compiled with optimization (- -O1and higher enables- -fomit-frame-pointer), gcc wouldn't be messing with RBP, and it would be zero when you ran- leave, thus directly causing a segfault.  (Because it does RSP=RBP and then uses the new RSP as the stack pointer for- pop %rbp.)
 
Anyway, if it doesn't fault, that will leave the stack pointer pointing to argc again, before the compiler-generated pop %rbp as part of the normal function epilogue.  So the compiler-generated ret will try to return to argv[0].  Since the stack is non-executable by default, that will segfault.  (And it's pointing to ASCII characters, which probably don't decode as useful x86-64 machine code.)
You could have found this out yourself by single-stepping the asm with GDB.  (layout reg and use stepi aka si).
In general you messing with the stack pointer and other registers behind the compiler's back will typically just make things crash.  And if there had been a return address higher up on the stack, pop %rcx would make a lot more sense than leave.
Footnote 1:
There's not even any machine code anywhere in the address space of your process that a useful return address could point to to make such a system call, unless you inject some machine code as an arg or environment variable.
You linked with -nostdlib so there's no libc linked.  If you did link libc dynamically but still wrote your own _start (e.g. with gcc -nostartfiles instead of the full -nostdlib), ASLR would mean the libc _exit function was at some runtime-variable address.
If you statically linked libc (gcc -nostartfiles -static), the code for _exit() wouldn't be copied into your executable unless you actually referenced it, which this code doesn't.  But you still need to get it called somehow; there's no return address pointing to it.