NASM: emit MSW of non-scalar (link-time) value

2019-08-15 12:38发布

问题:

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)

回答1:

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 for org, 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!