3 ms·
Cool. :) Nice article btw. I noticed a few other things in the section about segments: > The good news is that caring about them isn’t too bad: they essentia
by haberman 6y ago
Cool. :) Nice article btw.
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_developments 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 https://godbolt.org/z/jkt28n
- woodruffw 6y agoThanks for the kind words! 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 :-)
- cesarb 6y agoIf I recall correctly, at least on 32-bit mode the segment registers were a 13-bit offset into either the GDT or the LDT (so a maximum of 8192 entries on each), 1 bit to choose between the GDT and the LDT, and 2 bits for the protection ring. The base and limit (and other details) were loaded from the GDT/LDT when the segment register was loaded; the undocumented SAVEALL/LOADALL instructions could save and load the "hidden" base/limit/etc directly, leading to tricks like the "unreal mode".