I am attempting to define a constant IDT (Interrupt Descriptor Table) entry in NASM, and to do so, I need to emit into a data table the high word of a double-word address that is not resolved until link time. Is there a way to do it?
Here's the interrupt handler:
;;; Interrupt 3 (breakpoint) handler. For now, just poke the screen and halt.
align 8
int3:
mov [0xb8000],dword '* * '
hlt
And here's the IDT entry that references it. The most-significant and least-significant words of the offset need to be stored separately and non-contiguously:
;; Interrupt 3 - breakpoint
dw int3 ; offset (low) <---- WORKS
dw codesel ; code selector
db 0 ; unused
db 0b10001111 ; present, ring 0, 32-bit trap gate
dw int3 >> 16 ; offset (high) <---- ASSEMBLY ERROR
NASM correctly causes ld to emit the low word of int3's address, but the high word fails at assembly with this error:
pgm.asm:240: error: shift operator may only be applied to scalar values
NASM won't do math with a value that isn't defined until link time. I understand, but I need a way to work around this. I could:
- locate int3 absolutely
- Build the IDT at runtime instead of assembly time
I'll probably end up building the IDT at runtime, but it'd be good to know if there is a way to cause the assembler/linker to emit into a data table the high word of an address that is not resolved until link time.
Particulars:
- NASM 2.20.01
- nasm output format "aout"
- ld version 2.22
- 32-bit mode (nasm "bits 32" directive issued)
Well... as you probably know, Nasm will condescend to do a shift on the difference between two labels. The usual construct is something like:
dw (int3 - $$) >> 16
where
$$
refers to the beginning of the section. This calculates the "file offset". This is probably not the value you want to shift.dw (int3 - $$ + ORIGIN) >> 16
may do what you want... where
ORIGIN
is... well, what we told Nasm fororg
, if we were using flat binary. I ASSume you're assembling to-f elf32
or-f elf64
, telling ld--oformat=binary
, and telling ld either in a linker script or on the command line where you want.text
to be (?). This seems to work. I made an interesting discovery: if you tell ld-oformat=binary
(one hyphen) instead of--oformat=binary
(two hyphens), ld silently outputs nothing! Don't do this - you waste a lot of time!