I'm trying to write some special routine in assembly, for x86-64 (even x86 example is fine). The problem: my immediates are only resolved at link time.
For example,
addq $Label2-Label1, %rax
will add the difference between the two labels/symbols to rax. Unfortunately, because GNU Assembler only does one pass (I don't know why, even Open Source assemblers like FASM do multiple passes), it cannot resolve those so it will make the linker do it.
Unfortunately, it will reserve 4 bytes (32-bit immediate) for the linker, which is not what I want, because the difference in labels is always within -128 to +127 range.
My question, how do I force or specify that the instruction should have an 8-bit immediate? Like, what's the syntax here? In AT&T syntax or Intel, either is fine. For example, in NASM you do:
add rax, byte Label2-Label1
to specify 8bit immediate. But how to do this in GAS? What's the syntax to force it to use 8-bit immediate even if it doesn't know the immediate itself... I'd ideally want this in GAS, for specific reasons, so please don't tell me to use NASM as an answer!
EDIT: I'm sorry I forgot to mention, that yes, it is a "forward reference" here. Both labels are defined after the instruction, that is why GAS can't resolve them, I thought it's a relocation, but yes using '.byte label2-label1' works for sure as I have tested it, so I know it should be possible if it had some syntax for it...
If Label1
and Label2
are in the same source file then the problem doesn't seem to be related to the linker (GAS doesn't generate any relocations in that case) nor is it really due to GAS being a one pass assembler. It's smart enough to generate correct sized branches, which is a similar problem.
The problem comes down to GAS not being smart enough to choose the right sized instruction in cases other than jumps and branches, and not having any way to explicitly specify the size of the operand. The ".d8" displacement suffix is almost the syntax you want, but you're technically not using a displacement. But it wouldn't work anyways, as leal.d8 Label2-Label1(%eax),%eax
doesn't work, despite the fact a displacement is actually being used.
So that leaves really only one alternative, hand assembling the opcode. For example in 32-bit assembly:
.byte 0x83, 0xC0, (label2 - label1)
As for why GAS is only a one pass assembler, and in general doesn't do a number of things other assemblers, the answer is simple. It's primarily designed to assemble the output of GCC.