NASM - The Netwide Assembler

NASM Forum => Programming with NASM => Topic started by: Michael Mehlich on May 11, 2023, 03:34:23 PM

Title: How do I get the relative offset of a location from the beginning of a section?
Post by: Michael Mehlich on May 11, 2023, 03:34:23 PM
The Visual Studio linker supports thread local variables (for _declspec(thread)) by providing a section sorting mechanism. E.g. one can write the "segments" below and the linker will sort the segments starting with .tls based on the additional name fragments after the $ sign.
I'd like to get the relative offset of MyVariable (from the code below) wrt the .tls section as a constant, allowing me to write

   ; get pointer to thread local storage
   mov rsi, [gs:abs 0x58]     ; get pointer to thread local storage array
   mov eax, [rel _tls_index] ; get module-specific index to thread local storage
   mov rsi, [rsi+8*rax]         ; get pointer to thread local offset
   ; determine offset
        mov rcx, CONSTANT ; constant needed is relative offset of MyVariable wrt the beginning of the .tls section, (which starts at _tls_start in the example below)
   ; fetch value
   mov rax, [rsi+rcx] ; or even better mov rax, [rsi+CONSTANT] avoiding the preceding mov instruction

Is there a way to do this in nasm, or is the only way to get the offset to compute the difference between the address of MyVariable and the address of _tls_start?

Segment declarations

; in separately assembled file needing MyVariable

segment .tls$RTS data align=8
; note: place thread local data in this section
;   the linker sorts all .tls$... sections alphabetically, resulting in all data being placed between _tls_start and _tls_end
;   each thread has a thread local copy of this data, initialized by the origin value
MyVariable: dq 1

; in separately assembled file specifying the tls directory used by the linker and the windows program/dll loader

segment .rdata
   alignb 64, db 0
   extern _tls_used
_tls_used:
   istruc ImageTlsDirectoryStructure
      at ImageTlsDirectoryStructure.StartAddressOfRawData, dq _tls_start
      at ImageTlsDirectoryStructure.EndAddressOfRawData,   dq _tls_end
      at ImageTlsDirectoryStructure.AddressOfIndex,        dq _tls_index
      at ImageTlsDirectoryStructure.AddressOfCallBacks,    dq _tls_callbacks
      at ImageTlsDirectoryStructure.SizeOfZeroFill,        dd 0
      at ImageTlsDirectoryStructure.Characteristics,        dd 0
   iend

segment .tls$AAA data align=8
_tls_start:
   dq 0

segment .tls$ZZZ data align=8
_tls_end:
   dq 0
Title: Re: How do I get the relative offset of a location from the beginning of a section?
Post by: Frank Kotler on May 11, 2023, 08:17:33 PM
Nasm uses $$ to indicate the distance from the current location to the start of the section but I have NO idea how the Visual Studio linker will interact with this.

Best,
Frank

Title: Re: How do I get the relative offset of a location from the beginning of a section?
Post by: fredericopissarra on May 11, 2023, 11:40:01 PM
Since x86-64 mode uses RIP relative addresses and a FLAT model (i386 mode uses a flat model too), there's no need to reference the sections with wrt (with reference to) keyword. This:
Code: [Select]
; nasm -fwin64 -o test.obj test.asm
; Will create a COFF file.

section mysection rdata align=8

x:
  dq 1

section othersection data align=8

y:
  dq 2
Both sections will be created and all references to the symbols x and y will be RIP relative...

The linker sorts the sections as it seems fit and resolves the offsets. Notice your code won't touch any selector registers (since they are useless in x86-64 mode). In i386 mode those references to these symbols are relocated...
Title: Re: How do I get the relative offset of a location from the beginning of a section?
Post by: Michael Mehlich on May 12, 2023, 02:20:48 AM
Well, thread local storage in windows for C/C++ with _declspec(thread) is organized as follows
(1) the thread environment block contains a pointer to an array of pointers
(2) the variable _tls_index as specified in the tls image directory _tls_used is updated by the loader with a module-specific index (i.e. an index specific to the .exe or .dll)
(3) in the array of pointers from (1), at the _tls_index there is a pointer to the thread local storage for the module
(4) the storage can then be accessed using a field offset
I want to get to this relative address/field offset when mixing C/C++ and assembler code both defining their own thread local variables that go into the thread local storage section for the same module; so I need to be able to get the segment-relative address of the variable, or more precisely the relative address wrt the base segment name...

You can think of the aforementioned section sorting in the linker creating a struct and I want the offset of a particular field in that struct.
I can clumsily compute this offset myself by using the rip relative address of the variable and then subtract the rip relative address of the start location of the first of these related segments, but I'd prefer to directly access the offset.

(Side note: The actual data for the basic sorted segments with start/end addresses referenced by _tls_used is actually copied into the thread local storage area for the module upon thread startup, i.e. the thread-local data is initialized!)

I don't know how compiler, linker, and loader in the Visual Studio world integrate, but they appear to somehow achieve creating a single constant for the segment-relative address/field offset...

$$ unfortunately only provides me the relative offset wrt the beginning of the current full section name, i.e. in my original example wrt .tls$RTS, instead of the base section .tls; so I would actually need something like wrt .tls in x86-64 to get the offset I'm interested in.