Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does using jmp prevent the Clang assembler from figuring out an absolute expression for .fill?

I'm messing around with writing a simple toy boot loader (additional info at bottom of post). The following nasm code shows what the bootloader looked like at one point before I tried to switch to Clang. When compiled using nasm -f bin -o nasm.out boot.asm, then run using qemu-system-i386 nasm.out, prints an endless stream of ! characters to the screen:

bits 16
global main
main:
    mov ah, 0x0e
    mov al, '!'
    int 0x10
    jmp main
times 510 - ($ - $$) db 0x00
db 0x55
db 0xaa

I was curious if I could use Clang as my assembler instead of nasm, so I tried translating the program to what I think is the GAS syntax equivalent:

.code16
.global main
main:
    mov $0x0e, %ah
    mov $'!', %al
    int $0x10
    jmp main
.fill 510 - (. - main)
.byte 0x55
.byte 0xaa

However, when compiled using /usr/local/opt/llvm/bin/clang --target=i386-elf -m16 -c boot.s -o boot.o, I get the following error:

boot.s:9:7: error: expected absolute expression
.fill 510 - (. - main)
      ^

Compilation succeeds if jmp main is replaced with jmp *0x00 or a non-jmp instruction. It's not strictly equivalent, but it seems to point towards something about the label which is giving Clang issues.

nasm didn't have any issues figuring out how many bytes to use for padding. Why does Clang balk when I ask it to do the same? Is there some subtle (or obvious) thing which I missed?

I can always manually figure out how many bytes of padding I need, but that's tedious and error-prone, and seems like something the assembler should be able to do on its own.

I'm running Clang 4.0, installed through Homebrew, on macOS 10.12.6.


  • Quick bootloader refresher, if you need it: the boot sector is 512 bytes long, and needs to have the values 0x55 and 0xaa at offsets 510 and 511, respectively. The easy way of ensuring this is to make sure the binary output is exactly 512 bytes long, and has 0x55 and 0xaa as its last two bytes.
like image 582
awksp Avatar asked Jul 10 '17 18:07

awksp


1 Answers

It appears to be a bug in Apple's assembler. Although the assembler is a single pass, the calculation . - main should have resolvable to an absolute value.

You should be able to replace the .fill with an .org 510 directive to advance the location counter 510 bytes from the beginning of the current section. From the manual:

7.68 .org new-lc , fill

Advance the location counter of the current section to new-lc. new-lc is either an absolute expression or an expression with the same section as the current subsection. That is, you can’t use .org to cross sections: if new-lc has the wrong section, the .org directive is ignored. To be compatible with former assemblers, if the section of new-lc is absolute, as issues a warning, then pretends the section of new-lc is the same as the current subsection.

[snip]

Beware that the origin is relative to the start of the section, not to the start of the subsection. This is compatible with other people’s assemblers.

This should work:

.code16
.global main
main:
    mov $0x0e, %ah
    mov $'!', %al
    int $0x10
    jmp main
.org 510
.byte 0x55
.byte 0xaa

I was able to reproduce your problem with clang-900.0.39.2, and the usage of .org worked with that version.

like image 104
Michael Petch Avatar answered Sep 28 '22 08:09

Michael Petch