Home > database >  Masm string variable inside bootloader causes: Undefined symbol error
Masm string variable inside bootloader causes: Undefined symbol error

Time:10-22

For reasons I can not put my finger on, using a variable inside my bootloader causes it to be marked as an undefined symbol.

.386
option segment:use16
.model tiny, stdcall

;------------------------------
; CODE
;------------------------------
.code
org 07c00h

Print PROTO lpStr:WORD

_start:
    INVOKE Print, OFFSET bootInfo
    cli
    hlt

error:
    cli
    hlt

Print PROC USES ax si lpStr:WORD
    mov si, lpStr
    mov ah, 0Eh
    @@loop:
    lodsb
    cmp al, 0
    je @@done
    int 10h
    jmp @@loop
    @@done:
    ret
Print ENDP

;------------------------------
; DATA
;------------------------------
bootInfo db "Booting CompatOS...", 0

;------------------------------
; PADDING
;------------------------------
byte 510-($-_start) dup (0)
dw 0AA55h

END _start

I've searched and looked if there is something wrong with the way I declare my variable, but apparently, it should work like this. For context, I oriented myself on this.

Any help is much appreciated. Thanks.

Edit:

MASM and linker used to compile the sources. Makefile:

.PHONY: clear
clear:
    rm -f -r -d ./tmp/
    rm -f -r -d ./bin/

.PHONY: build_debug
build_debug:
    mkdir -p tmp
    mkdir -p bin
    masm_615/bin/ml /nologo /AT /c /Febin\\CompatOS.img /Fotmp\\boot.obj src\\boot\\boot.asm
    masm_615/bin/link /nologo /TINY /NOD tmp\\boot.obj, bin\\CompatOS.img, NUL, NUL, NUL

Errors:

1>src\boot\boot.asm(14): error A2006: undefined symbol : bootInfo
1>src\boot\boot.asm(14): error A2114: INVOKE argument type mismatch : argument : 1

CodePudding user response:

The errors are because you're using the invoke directive with a forward reference. bootInfo is declared after you use invoke. The issue has been discussed on the MASM32 forum. One way to resolve this is not use invoke and do a call. This would require you having to push the address of bootInfo on the stack yourself like this:

;   INVOKE Print, OFFSET bootInfo
    mov ax, OFFSET bootInfo
    push ax                       ; Push register since original 8086
                                  ; didn't have a push IMMediate instruction
    call Print

Another coding option is to move the data to near the beginning of your bootloader and the first instruction of the bootloader jumps over the data to the code. This would put bootInfo before the invoke and you'd no longer have a forward reference. Something like this:

;------------------------------
; CODE
;------------------------------
.code
org 07c00h

Print PROTO lpStr:WORD

_start:
    jmp skipdata
    ;------------------------------
    ; DATA
    ;------------------------------
    bootInfo db "Booting CompatOS...", 0

skipdata:
    INVOKE Print, OFFSET bootInfo
    cli
    hlt

Another way to solve this is to use JWASM (an open source MASM compatible assembler). I noticed JWASM has no problem with forward references when using invoke.


Note: I realize you are somewhat constrained because this is a bootloader and you can't just put bootInfo in a .data section before the .code section because you want to make sure the 0xaa55 signature is placed exactly where it is needed with the data and code before it.

I recommend taking a look at my bootloader tips for common problems when developing a bootloader, including suggestions when testing on real hardware.

You may wish to use the .8086 directive rather than .386 if you intended to run on real hardware that may predate the 386. Legacy bootloaders are generally developed with the lowest common denominator in mind - the 8086 processor. If you don't intend to run on ancient hardware then this may not be an issue.

  • Related