Search code examples
assemblyincludenasm

How can one include only specified labels from one file to another in NASM?


Let's suppose that you have a library.asm with a function that has a loop:

section   .text 

useful_func:
    push rbp
    mov rbp, rsp

    loop0:
        ; Do useful stuff...
        jnz loop0

    mov rsp, rbp
    pop rbp
    ret

and a main.asm that includes the library and calls that function:

%include "library.asm"

section   .text
global    _start 

_start:
    ; Do stuff
    call useful_func
    ; Do more stuff

Of course, as a user of library.asm, you're not really interested in the label loop0, you're actually interested in useful_func. However, both labels are imported. So if you change your main.asm to:

%include "library.asm"

section   .text
global    _start 

_start:
    ; Do stuff
    loop0:
        call useful_func
        jnz loop0
    ; Do more stuff

You get an error, since the label loop0 was defined twice.

So, is there any way to specify to NASM which labels are useful and should be exported/imported, and which ones shouldn't?

Note: There is no necessity for the use of the %include command, it's simply the only way that I know to import from another file in NASM. If there is a better and more flexible command/way to do that, it's also appreciated.


Solution

  • Use local labels. nasm.us/doc/nasmdoc3.html section 3.9

    Local labels start with a ., and can be used as many times as you want on the code. Each local label is tied to the last non-local label. On this example:

    1 label1:
    2     .loop:            ; Implicitly label1.loop
    3         ;stuff
    4 label2:
    5     .loop:            ; Implicitly label2.loop
    6         ;other stuff
    7 jmp .loop             ; Implicitly label2.loop
    

    line 7 will jump to line 5, since both of them are local and after label2. So you can change the labels that should not be external to local, and no conflict will happen:

    library.asm

    section   .text
    
    useful_func:
        push rbp
        mov rbp, rsp
    
        .loop0:                      ; Now local
            ; Do useful stuff...
            jnz .loop0
    
        mov rsp, rbp
        pop rbp
        ret
    

    main.asm

    %include "library.asm"
    
    section   .text
    global    _start       
    
    _start:
        ; Do stuff
        .loop0:
            call useful_func
            jnz .loop0
        ; Do more stuff
    

    Another way to solve this is to mark the labels you want to export as global and then link the files together instead of relying on the %include directive.

    If you don't mind changing your linkage and compilation (e.g. from nasm -f elf64 main.asm && ld main.o -o main to nasm -f elf64 library.asm && nasm -f elf64 main.asm && ld library.asm main.o -o main), then you can use the global/extern directives (read this doc sections 6.5 and 6.6 for more details) instead of %include.

    Using this method, you will only export the specific labels declared with global/extern. So you can use it to avoid the conflict:

    library.asm

    global useful_func    ; Now global
    
    section   .text
    
    useful_func:
        push rbp
        mov rbp, rsp
    
        loop0:
            ; Do useful stuff...
            jnz loop0
    
        mov rsp, rbp
        pop rbp
        ret
    

    main.asm

    extern useful_func    ; Now extern
    
    section   .text
    global    _start
    
    _start:
        ; Do stuff
        loop0:
            call useful_func
            jnz loop0
        ; Do more stuff
    

    Note: Even if you decide to use this method, I'd suggest making your loops/conditionals local anyway, so you can reuse the labels.