I noticed a few other things in the section about segments:
> The good news is that caring about them isn’t too bad: they essentially boil down to adding the value in the segment register to the rest of the address calculation.
Surprisingly, the segment register's value isn't added directly (my coworker and I discovered this recently). The segment bases are stored in model-specific registers, and I don't believe these are readable from user-space. https://en.wikipedia.org/wiki/X86_memory_segmentation#Later_...
If you try to read %fs directly, you'll get a completely unrelated value (an offset into table I think?)
Another surprising and unfortunate thing is that segment-qualified addresses don't work with lea. That means getting the address of a thread-local unfortunately is not as simple as "lea rax, fs:[var]." You actually have to do a load to get the base address of the thread-local block (eg. fs:0). The first pointer in the thread-local block is reserved for this. That's why this function has to do a load before the lea: https://godbolt.org/z/jkt28n
And yep! I need to make the language around the segment registers more precise: if I'm remembering right, the segment value itself gives you the GDT index (maybe only in 32-bit mode?), which you can then pull from.
For 64-bit modes I think those values essentially become nonsense because of the fsbase/gsbase MSRs, as you mentioned :-)