How to get RVA in EuroAssembler?

Problems which concern EuroAssembler
AndreyDmitriev
Posts: 13
Joined: 01 Jun 2023 09:11

How to get RVA in EuroAssembler?

Unread postby AndreyDmitriev » 23 Oct 2025 19:04

I trying to get SEH running in Assembly (Windows, x64).
Everything works as expected, but I unable to get RVA for my try... section, handler and unwind struct. For the moment, just for test, I placed hard coded constants, and it works, but how to do this in proper way?
Code:
EUROASM CPU=X64, SIMD=AVX2
%^SourceName PROGRAM Format=PE, Width=64, Model=Flat, IconFile=, Entry=Start
INCLUDE winscon.htm, winabi.htm, cpuext64.htm

[.text]
Start:
nop
StdOutput helloMsg

try:
MOV ECX,0x40000000 ; Instructions Counter
RDPMC ; EXCEPTION_PRIV_INSTRUCTION (0xC0000096)

safe_place:
StdOutput finishMsg
TerminateProgram

handler:
SUB RSP,8*(4+1) ; 0x0F8 is offset to CONTEXT64.Rip
mov [R8+0x0F8], safe_place, DATA=Q ; safe_place = 0x40101F
StdOutput exceptionMsg
XOR EAX,EAX
ADD RSP,8*(4+1)
retn

[.data]
helloMsg D "Hello, SEH!",10,0
finishMsg D "Sucessfully finished",10,0
exceptionMsg D "Instruction caused exception",10,0
align 4 ; read somewhere about alignment
UNWIND DB 0x19,0,0,0 ; Hard coded for the moment
;DD handler - %^IMAGEBASE
DD 0x1044 ; DD RVA handler
DD 0

[.pdata] SEGMENT PURPOSE=EXCEPTION
DD 0x1018 ; try
DD 0x101F ; safe_place
DD 0x2044 ; UNWIND
; ???
ENDPROGRAM

How to replace all these "DD 0x1044" with something like "DD handler - %^IMAGEBASE" or may be "DD RVA handler"? I tried different syntax, but nothing works.
User avatar
vitsoft
Site Admin
Posts: 57
Joined: 04 Nov 2017 20:08
Location: Vítkov, The Czech Republic
Contact:

Re: How to get RVA in EuroAssembler?

Unread postby vitsoft » 23 Oct 2025 20:08

How to replace all these "DD 0x1044"
Perhaps instead of asm-time compute the constant at run-time? RVA already is related to %^ImageBase, perhaps something like

Code: Select all

MOV EAX, OFFSET# handler
and then

Code: Select all

MOV [UNWIND + 4],EAX
might do the trick?
I notice at StackOverflow that you have solved it with FASM, but I don't understand it at all. Neither do I know what structure is your UNWIND, what is expected in section [.pdata] or why are the offsets DWORD and not QWORD in 64-bit system.

AI says about interrupt & exceptions:

3. Interrupt Handling Flow
Here’s what happens when an interrupt or exception occurs:
CPU detects the interrupt (either externally or internally).
Current privilege level (CPL) and IDT entry DPL are checked:
If CPL > DPL → privilege transition (e.g., from user to kernel).
CPU switches to the target stack:
If privilege level changes, uses the RSP0 field from the TSS.
If an IST entry is specified, uses that stack instead.
CPU pushes context onto the new stack:
For exceptions: pushes RIP, CS, RFLAGS, and optionally an error code.
Handler executes — kernel code handles the interrupt.
Handler issues iretq:
CPU restores RIP, CS, RFLAGS, and possibly the stack pointer and privilege level.

I'm afraid that SEH is managed differenly in Windows, Linux, Mac, so it cannot be build to €ASM. Perhaps some library, but I don't know what should be in the section [.pdata]. My documentation of PE/DLL does not say a word about it.
Currently I'm finishing a new version of €ASM but SEH will not be in it yet, I'm sorry.
AndreyDmitriev
Posts: 13
Joined: 01 Jun 2023 09:11

Re: How to get RVA in EuroAssembler?

Unread postby AndreyDmitriev » 24 Oct 2025 09:04

vitsoft wrote: 23 Oct 2025 20:08 Currently I'm finishing a new version of €ASM but SEH will not be in it yet, I'm sorry.
Thank you for the quick response!

Just to clarify — no, no, I'm not asking for SEH to be implemented in EuroAssembler at all. That would be overengineering, and SEH is handled differently on different platforms.

However, it would be great to have the ability to handle relative addresses with lower bit-widths.

The trick of setting the handler address at run-time works for the unwind structure, but unfortunately not for the exception data structure, since it's part of the PE header and must be defined at "compile" time.

In general, SEH works in a relatively straightforward way. Here's the documentation - https://learn.microsoft.com/en-us/cpp/b ... w=msvc-170.

There are three 32-bit fields in .pdata section:

| ULONG | Function start address (begin of "critical" section)
| ULONG | Function end address (end of "critical" section)
| ULONG | Unwind info address (where address of the handler stored)

All three addresses are image-relative, meaning they are 32-bit offsets from the base address of the image containing the function table entry.
And yes, even in 64-bit applications, these fields are 32-bit.

Physically they written in executable at offset 0x800
0x0000000800: 18 10 00 00 1F 10 00 00 44 20 00 00 ...
And EuroAssembler did this correctly in [.pdata] SEGMENT PURPOSE=EXCEPTION
But unfortunately DD handler - %^IMAGEBASE is not allowed, that the problem.

Basically, if I try to assign a value that doesn't fit the specified data type, EuroAssembler correctly raises an error. For example:
DD 0x100000000 ; -> E6729 Data value "0x100000000" is too big for datatype "D".
However, if I try something like:
DD handler - %^IMAGEBASE
Then I get one warning and one error:
W3529 64bit symbol does not match datatype "D".
E6729 Data value "handler - 4194304" is too big for datatype "D".

This happens because the EuroAssembler checks the data types, but not the actual computed values behind. While it's true that subtracting two 64-bit values *might* result in a value that doesn't fit into 32 bits, in this specific case, the result of `handler - %^IMAGEBASE` is only `0x10B5` (4277 decimal), which clearly fits.

So, I'm suggesting to add some "intelligence" to this check — allow `DD` when the computed value fits, even if the operands are 64-bit.

By the way, `DW` would also work here, but obviously not `DB`:
DB handler - %^IMAGEBASE
This correctly should raise error something like this:
E6729 Data value "handler - 4194304" (4277) is too big for datatype "B".

Since the handler address is known at compile time and everything is static, I’m asking for the EuroAssembler to evaluate the expression and raise errors based on the *actual value*, not just the operand types, this will allow to fill Exception data structure correctly.

Small code snippet:
EUROASM AutoSegment=Yes, CPU=X64, SIMD=AVX2
%^SourceName PROGRAM Format=PE, Width=64, Model=Flat, IconFile=, Entry=Start
INCLUDE winscon.htm, winabi.htm, cpuext64.htm

handlerAdr D "Address of handler is 0x",0
handlerRva D "RVA Address of handler is 0x",0
Buf DB 8 * B

DQ handler - %^IMAGEBASE ; This is OK, but too wide for SEH purposes
DD handler - %^IMAGEBASE ; This is needed, but unfortunately is not allowed:
;W3529 64bit symbol does not match datatype "D".
;E6729 Data value "handler - 4194304" is too big for datatype "D".
; But Data value "handler - 4194304" is 0x000010B5 - its FIT into "D"

Start:nop
MOV RAX, handler ; Address
StoH Buf
StdOutput handlerAdr, Buf, Eol=Yes, Console=Yes

MOV RAX, handler - %^IMAGEBASE ; RVA
StoH Buf
StdOutput handlerRva, Buf, Eol=Yes, Console=Yes

TerminateProgram

handler:
nop ; just dummy
retn

ENDPROGRAM


Output:
>rvademo.exe
Address of handler is 0x004010B5
RVA Address of handler is 0x000010B5


And, by the way, this is allowed in EuroAssembler and handled correctly:
MOV EAX, handler - %^IMAGEBASE ; RVA as 32-bit
StoH Buf32
StdOutput handlerRva, Buf32, Eol=Yes, Console=Yes

So, in theory should be possible also in DD handler - %^IMAGEBASE form.

--- UPDATE ---
Just got it running with OFFSET#

minimal code:
EUROASM CPU=X64, SIMD=AVX2
%^SourceName PROGRAM Format=PE, Width=64, Model=Flat, IconFile=, Entry=Start
INCLUDE winscon.htm, winabi.htm, cpuext64.htm

[.text]
Start:nop
StdOutput helloMsg, Eol=Yes, Console=Yes
try:
MOV ECX,0x40000000 ; Instructions Counter
RDPMC ; EXCEPTION_PRIV_INSTRUCTION (0xC0000096)

safe_place:
StdOutput finishMsg, Eol=Yes, Console=Yes
TerminateProgram

handler:
SUB RSP,8*(4+1) ; 0x0F8 is offset to CONTEXT64.Rip
mov [R8+0x0F8], safe_place, DATA=Q ; safe_place = 0x40101F
StdOutput exceptionMsg, Eol=Yes, Console=Yes
XOR EAX,EAX
ADD RSP,8*(4+1)
retn

[.data]
helloMsg D "Hello, SEH!",0
finishMsg D "Sucessfully finished",0
exceptionMsg D "Instruction caused exception",0
align 4 ; The RUNTIME_FUNCTION structure must be DWORD aligned
UNWIND DB 0x19,0,0,0 ; Version & Flags hard coded for the moment
DD (OFFSET# handler) + 0x1000

[.pdata] SEGMENT PURPOSE=EXCEPTION
DD (OFFSET# try) + 0x1000
DD (OFFSET# safe_place) + 0x1000
DD (OFFSET# UNWIND) + 0x2000 ; virtual address of .data is 0x2000

ENDPROGRAM


and result:
>seh4.exe
Hello, SEH!
Instruction caused exception
Sucessfully finished


But the OFFSET# is not RVA, so I have to add 0x1000 (and 0x2000 for UNWIND_INFO).
May be something like RVA#:
DD RVA# try
DD RVA# safe_place
DD RVA# UNWIND ; virtual address of .data, set correct

which will act exactly the same as <address> - %^IMAGEBASE and be more elegant, because hard coded 0x1000 and 0x2000 are not good idea.
User avatar
vitsoft
Site Admin
Posts: 57
Joined: 04 Nov 2017 20:08
Location: Vítkov, The Czech Republic
Contact:

Re: How to get RVA in EuroAssembler?

Unread postby vitsoft » 24 Oct 2025 15:07

Relative virtual address (RVA) can be handled by preprocessing aparatus, with %SETA OFFSET# variable + RVA_of_its_section.
Offset is refined in each assembly pass until it is fixed in the final pass. Only then the linking starts and uses the RVA.
I tried to put such RVA to the section [.pdata] and it seems to work.
This can be checked directly in the executable file, the section [pdata] (at file offset 0x0A00 in this example) contains 3 DWORDs with values 0x1025, 0x102C, 0x3000.
EUROASM CPU=X64, SIMD=AVX2
%^SourceName PROGRAM Format=PE, Width=64, Model=Flat, IconFile=, Entry=Start
INCLUDE winscon.htm, winabi.htm, cpuext64.htm

[.text]
Start:
nop
StdOutput =B"Hello, SEH", Eol=yes

try:
MOV ECX,0x40000000 ; Instructions Counter
RDPMC ; EXCEPTION_PRIV_INSTRUCTION (0xC0000096)

safe_place:
StdOutput =B"Sucessfully finished", Eol=yes
TerminateProgram

handler:
SUB RSP,8*(4+1) ; 0x0F8 is offset to CONTEXT64.Rip
mov [R8+0x0F8], safe_place, DATA=Q
StdOutput =B"Instruction caused exception", Eol=yes
XOR EAX,EAX
ADD RSP,8*(4+1)
retn

[.data]
align 4 ; read somewhere about alignment
UNWIND DB 0x19,0,0,0 ; Hard coded for the moment
%HandlerRVA %SETA OFFSET# handler + 0x1000
DD %HandlerRVA  ; 0x1044 ; DD RVA handler
DD 0

[.pdata] SEGMENT PURPOSE=EXCEPTION
%TryRVA %SETA OFFSET# try + 0x1000                         ; 0x1000 is RVA of section [.text]
DD %TryRVA                                       
%Safe_placeRVA %SETA OFFSET# safe_place + 0x1000  ; 0x1000 is RVA of section [.text]
DD %Safe_placeRVA                                      
%UnwindRVA  %SETA OFFSET# UNWIND + 0x3000         ; 0x3000 is RVA of section [.data]
DD %UnwindRVA                                   
ENDPROGRAM
Unfortunalely this solution needs to hardcode RVA of sections [.text] and [.data] (numbers 0x1000 and 0x3000) into the source,
but these two numbers are easy to obtain by subtracting ImageBase=0x40_0000 from the field VA= in the ListMap of the executable %SourceName.asm.lst.

€ASM linker by default links at RVA=0 those components
  • MZ DOS header
  • PE header
  • COFF file header
  • PE Optional header
  • COFF section headers
  • Alignment to SectionAlign (to 0x1000)
Those component should altogether fit to 0x1000 in most cases, so if you dont change the default order of sections, RVA of [.text]=0x1000 should be a constant. With RVA od [.data] it is a little more complicated, you need to inspect the listing, adjust RVA and then recompile.

However, I still don't understand the code in your handler, the structure of CONTEXT64, why mov [R8+0x0F8], safe_place, DATA=Q,
who miraculously set R8 with what...
AndreyDmitriev
Posts: 13
Joined: 01 Jun 2023 09:11

Re: How to get RVA in EuroAssembler?

Unread postby AndreyDmitriev » 24 Oct 2025 15:18

vitsoft wrote: 24 Oct 2025 15:07
However, I still don't understand the code in your handler, the structure of CONTEXT64, why mov [R8+0x0F8], safe_place, DATA=Q,
who miraculously set R8 with what...
Oh, it is quite simple.
In Windows x64 SEH, when an exception occurs, the system calls my exception handler with the following calling convention:
LONG handler(
PEXCEPTION_RECORD ExceptionRecord,
ULONG64 EstablisherFrame,
PCONTEXT ContextRecord,
PDISPATCHER_CONTEXT DispatcherContext
);

These parameters are passed in registers as follows (according to x64 calling conventions):

| `ExceptionRecord` > RCX
| `EstablisherFrame` > RDX
| `ContextRecord` > **R8**
| `DispatcherContext` > R9

So, `R8` holds a pointer to the `CONTEXT` structure, which contains the CPU state at the time of the exception — including registers, flags, and most importantly, the instruction pointer (`Rip`). The offset `0x0F8` corresponds to the `Rip` field within the `CONTEXT` structure. As a result, this instruction modifies the saved instruction pointer to point to `safe_place`, effectively telling the OS to resume execution at `safe_place` after the exception is handled.

In the vectorized example (posted on StackOverflow), I need to do this manually by incrementing the pointer by 2 to move past the instruction that caused the exception.
AndreyDmitriev
Posts: 13
Joined: 01 Jun 2023 09:11

Re: How to get RVA in EuroAssembler?

Unread postby AndreyDmitriev » 24 Oct 2025 19:00

And thank you for the example, but here is a small technical trouble: the .data section is now located at 0x3000 because you added constants directly to StdOutput. As a result, they were placed in the .rodata section. This additional section shifted .data from 0x2000 to 0x3000:
;...
handler:
SUB RSP,8*(4+1) ; 0x0F8 is offset to CONTEXT64.Rip
mov [R8+0x0F8], safe_place, DATA=Q
StdOutput =B"Instruction caused exception", Eol=yes
XOR EAX,EAX
ADD RSP,8*(4+1)
retn

[.data]
align 4 ; The RUNTIME_FUNCTION structure must be DWORD aligned
UNWIND DB 0x19,0,0,0 ; Version & Flags hard coded for the moment
DD OFFSET# handler + 0x1000

[.pdata] SEGMENT PURPOSE=EXCEPTION
DD OFFSET# try + 0x1000
DD OFFSET# safe_place + 0x1000
DD OFFSET# UNWIND + 0x3000 ; !!!

ENDPROGRAM


Previously, the constants were located in .data at 0x2000 (since there was no .rodata section):

;...
handler:
SUB RSP,8*(4+1) ; 0x0F8 is offset to CONTEXT64.Rip
mov [R8+0x0F8], safe_place, DATA=Q ; safe_place = 0x40101F
StdOutput exceptionMsg, Eol=Yes
XOR EAX,EAX
ADD RSP,8*(4+1)
retn

[.data]
helloMsg D "Hello, SEH!",0
finishMsg D "Sucessfully finished",0
exceptionMsg D "Instruction caused exception",0
align 4 ; The RUNTIME_FUNCTION structure must be DWORD aligned
UNWIND DB 0x19,0,0,0 ; Version & Flags hard coded for the moment
DD OFFSET# handler + 0x1000

[.pdata] SEGMENT PURPOSE=EXCEPTION
DD OFFSET# try + 0x1000
DD OFFSET# safe_place + 0x1000
DD OFFSET# UNWIND + 0x2000 ; !!!


As a result, from a programming point of view, we should always keep the overall layout in mind and adjust accordingly.
Therefore, I vote for RVA#.

I added this definition to dict.htm at the end of this list:

DictUnaryOperators:: ; Data = priority + operator flags
Dict Operator,"-", Data=13+dictUnary, DataName=Minus
Dict Operator,"+", Data=13+dictUnary, DataName=Plus
Dict Operator,"~", Data=08+dictUnary, DataName=BitwiseNot
Dict Operator,"!", Data=04+dictUnary, DataName=LogicalNot
Dict Operator,"SIZE#", Data=15+dictUnary, DataName=Size
Dict Operator,"TYPE#", Data=15+dictUnary, DataName=Type
Dict Operator,"REGTYPE#", Data=15+dictUnary, DataName=RegType
Dict Operator,"SCOPE#", Data=15+dictUnary, DataName=Scope
Dict Operator,"OFFSET#", Data=15+dictUnary, DataName=Offset
Dict Operator,"SECTION#", Data=15+dictUnary, DataName=Section
Dict Operator,"SEGMENT#", Data=15+dictUnary, DataName=Segment
Dict Operator,"GROUP#", Data=15+dictUnary, DataName=Group
Dict Operator,"PARA#", Data=15+dictUnary, DataName=Para
Dict Operator,"FILESIZE#",Data=15+dictUnary, DataName=FileSize
Dict Operator,"FILETIME#",Data=15+dictUnary, DataName=FileTime
Dict Operator,"RVA#", Data=15+dictUnary, DataName=Rva ; .AD.
Dict End


and in a few other places, so now this code compiles technically withiut errors and still functional:

[.data]
align 4
UNWIND DB 0x19,0,0,0
DD RVA# handler + 0x1000

[.pdata] SEGMENT PURPOSE=EXCEPTION
DD RVA# try + 0x1000 ; 0x1000 is the RVA of section [.text]
DD RVA# safe_place + 0x1000 ; 0x1000 is the RVA of section [.text]
DD RVA# UNWIND + 0x3000 ; 0x3000 is the RVA of section [.data]
ENDPROGRAM


But at this moment, #RVA behaves exactly same like #OFFSET (because copied from OFFSET#), and I am not sure where in the source code this behavior needs to be changed so that "DD RVA# MyAddress" evaluates to (MyAddress - %^IMAGEBASE) or equivalently (OFFSET# MyAddress + <correct RVA of the section to which the address belongs>). I will appreciate if you will just point me to the source where OFFSET# calculated and I'll do similar for RVA#. All what I wanted is to avoid hard coded 0x1000, 0x2000 and 0x3000, that is all.
User avatar
vitsoft
Site Admin
Posts: 57
Joined: 04 Nov 2017 20:08
Location: Vítkov, The Czech Republic
Contact:

Re: How to get RVA in EuroAssembler?

Unread postby vitsoft » 24 Oct 2025 20:27

.data section is now located at 0x3000 because you added constants directly to StdOutput. As a result, they were placed in the .rodata section.
This is because of literals, which are by default being put to [.rodata]. They might be placed in [.data] if you redefine the data section:
[.data] SEGMENT PURPOSE=DATA+LITERAL.
I don't think that it is a problem anyway, 0x2000 or 0x3000, who cares? Of course you might avoid the literals and use named constants as in you first example.
what I wanted is to avoid hard coded 0x1000, 0x2000 and 0x3000,


This seems legit, I consider to add a new attribute RVA# which would return offset + segment - imagebase as a plain number (similar to OFFSET#).
But it is not that simple as just to add the new word RVA# to dictionary. Attributes are handled in module exp.htm, mostly in the procedure ExpEval. Then the new feature also affects tests, examples, documentation in two languages...
If you don't want to wait for the new release and add RVA# by yourself, it might help to search for a similar attribute, say REGTYPE, with checked Embedded word and Case insensitive:
https://euroassembler.eu/search.php?CI&EW&q=REGTYPE

Who is online

Users browsing this forum: No registered users and 1 guest