assemblyriscvgnu-assemblerweakriscv32

assembly weak symbol not working as (I) expected


I am cross compiling with riscv-corev32-elf-gcc.

In a first file 'generic.S' I have the following code:

.section .text.handlers
.weak u_sw_irq_handler
.weak __no_irq_handler

.section .vectors, "ax"
vector_table:
    jal x0, u_sw_irq_handler 
    jal x0, __no_irq_handler     
    jal x0, __no_irq_handler
    ...


.section .text
reset_handler:
    la t0, vector_table
    csrw mtvec, t0

In a second file 'custom.S' I have the following code:

.section .text.handlers
.<x> u_sw_irq_handler
.<x> __no_irq_handler

.section .vectors, "ax"
vector_table:
    jal x0, u_sw_irq_handler 
    jal x0, ISR_1_handler     
    jal x0, ISR_2_handler
    ...

where <x> is either global or local

when global, this is the (partial) result of the disassembly:

00001000 <vector_table>:
    1000:   7760d06f            j   e776 <u_sw_irq_handler>
    1004:   1880706f            j   81cc <ISR_1_handler>
    1008:   1e40706f            j   822c <ISR_2_handler>
    ...
    1084:   7760d06f            j   e776 <u_sw_irq_handler>
    1088:   7320d06f            j   e736 <__no_irq_handler>
    108c:   72e0d06f            j   e736 <__no_irq_handler>
    ...


00001108 <reset_handler>:
    1108:   00000297            auipc   t0,0x0
    110c:   f7c28293            addi    t0,t0,-264 # 1000 <vector_table>
    1110:   30529073            csrw    mtvec,t0

when local, this is the (partial) result of the disassembly:

00001000 <vector_table>:
    1000:   7760d06f            j   e776 <u_sw_irq_handler>
    1004:   1880706f            j   81cc <ISR_1_handler>
    1008:   1e40706f            j   822c <ISR_2_handler>
    ...

00001084 <vector_table>:
    1084:   7760d06f            j   e776 <u_sw_irq_handler>
    1088:   7320d06f            j   e736 <__no_irq_handler>
    108c:   72e0d06f            j   e736 <__no_irq_handler>
    ...



00001108 <reset_handler>:
    1108:   00000297            auipc   t0,0x0
    110c:   f7c28293            addi    t0,t0,-132 # 1084 <vector_table>
    1110:   30529073            csrw    mtvec,t0

But this not what I want:

edit: sorry I missed matched the logs for the reset handler disassembly


Solution

  • assembly weak symbol not working as I expected

    weak works differently than you think:

    weak makes a symbol global if the symbol does not exist in another object file and it makes a symbol itself (!) disappear if it exists in another object file.

    Example (sorry that my examples are x86 because I don't know riscv32):

    .weak some_irq_handler
    .weak some_other_irq_handler
    
    vector_table:
        jmp some_irq_handler
        jmp some_other_irq_handler
    
    some_irq_handler:
        iret
    
    some_other_irq_handler:
        iret
    

    If no other object file contains .global some_irq_handler nor .global some_other_irq_handler, the .weak lines nearly have the same effect as .global lines.

    However, if another object file contains these two symbols, the example above is more or less equal to:

    vector_table:
        # This symbol is defined in another object file
        jmp some_irq_handler
        # This symbol is defined in another object file
        jmp some_other_irq_handler
    
        # The code of "some_irq_handler" is still
        # present; only the label "some_irq_handler:"
        # has been removed by the linker!
        iret
    
        iret
    
    # -- Another object file --
    some_irq_handler:
        ...
    

    I want the generic definition to be overwritten by the custom.S definition if it exists ...

    Case 1: The vector_table needs not to be located at a certain address in memory:

    In this case you can do it the following way:

    .weak vector_table
    
    vector_table:
        jmp no_interrupt
        jmp no_interrupt
        ...
    

    If another vector table is present in you project, the resulting code (after linking) will look like this:

    # -- "weak" object file --
    # vector_table: - Label removed by the linker
    # but the table itself is still there
        jmp no_interrupt
        jmp no_interrupt
        ...
    # -- "overwriting" object file --
    vector_table:
        jmp interrupt1
        jmp interrupt2
        ...
    

    Case 2: The vector_table needs to be located at a certain address in memory:

    In this case, you can do it the following way:

    .weak interrupt1
    .weak interrupt2
    ...
    
    vector_table:
       jmp interrupt1
       jmp interrupt2
       ...
    
    __no_irq_handler:
    interrupt1:
    interrupt2:
       ...
    interrupt256:
       iret
    

    You will never overwrite the "generic" interrupt vector table by another one but you only overwrite the symbols interrupt<n>:

    .global interrupt2
    
    interrupt2:
        mov al, 0x1234
        out 0xE4, ax
        mov al, 0x20
        out 0x20, al
        iret
    

    (This will also work with C-written code. Because the C compiler makes functions .global and not .weak by default, the C-written function overwrites the .weak one.)

    The resulting code will then look like this:

    # -- "weak" object file --
    vector_table:
       jmp interrupt1
       jmp interrupt2
       ...
    
    __no_irq_handler:
    interrupt1:
    # interrupt2: - Label removed by the linker
    interrupt3:
       ...
    interrupt256:
       iret
    
    # -- "overwriting" object file --
    interrupt2:
        mov al, 0x1234
        out 0xE4, ax
        mov al, 0x20
        out 0x20, al
        iret