Using .reloc from assembly

942 views Asked by At

This is a cut-down version of the problem I'm facing on AArch64:

I've this macro that keeps dumping some data into a section.

#define GEN_DATA(_type) \
    .pushsection .mydata_##_type, "aw"; \
    .ifndef start; \
    start:; \
    .endif; \
    .word 1; \
    .reloc end_loc, R_AARCH64_ABS64, .; \
    .popsection

I eventually wanted to capture the start and end of similar types in a structure like this:

    .pushsection .head, "aw"
    .quad start
    end_loc:
    .quad 0
    .popsection

I can track where the section starts with the start symbol. I don't know in advance how many invocations of GEN_DATA() will be there in the build, so I can't define an end. I don't know how many _types of section will be used either, so can't place guard symbol script. Therefore I decided to leave a relocation entry for end_loc so that linker will eventually fixup where the whole section ends. Yes, there will be multiple relocation entries for the same end_loc, but because they're absolute relocations, I reckon they don't conflict.

I had hunch, but in the final binary, end_loc is getting fixed up with a wrong address. I'll blame it on multiple relocation entries, but the weird thing is that, everything is OK if I also add a dummy, extra relocation entry too -- I.e. I modify the structures above to:

#define GEN_DATA(_type) \
    .pushsection .mydata_##_type, "aw"; \
    .ifndef start; \
    start:; \
    .endif; \
    .word 1; \
    .reloc end_loc, R_AARCH64_ABS64, .; \
    .reloc dummy_loc, R_AARCH64_ABS64, .; \
    .popsection

and:

    .pushsection .head, "aw"
    .quad start
    end_loc:
    .quad 0
    dummy_loc:
    .quad 0
    .popsection

So I'm wondering:

  • Why is end_loc getting fixed up wrongly? What's wrong with multiple absolute relocation entries, if at all? Isn't the linker expected to go through them in order, and the last one falls into effect?

  • Why would simply adding a dummy relocation make everything right?

Basically, what's going on?!

And finally, are there any alternatives approaches that I could try?

EDIT: I've now pushed the sample code to a Git repository. Use make and make broken=1 to view the disassembly. Needs Linaro AArch64 tool chain in $PATH.

1

There are 1 answers

0
Ross Ridge On

I don't know what's going on with the relocations, but the easiest way to do what you're trying accomplish is to use a linker script. This will let you group all the .mydata_XXX_type sections together and provide symbols for the start and end of the grouped sections. Something like this:

SECTIONS
{
    .mydata :
    {
        start   = .;
        *(.mydata*);
        end_loc = .;
    }
}

Which you would use with an assembly file like this:

    .macro gen_data, type
    .pushsection .mydata_\()\type\()_type, "aw"
    .word   1
    .popsection
    .endm

    .text
    gen_data foo
    gen_data bar
    gen_data baz

    .section .head, "aw"
    .quad   start
    .quad   end_loc

(I've used assembler macros instead of C macros because they're much easier to work with.) You'd use the two above files like this:

    as -o test.o test.s
    ld -o test test.o test.ld

If you know all the possible section "types" then you do it without using a linker script by relying on the fact the linker places unknown sections in the order it first encounters them. For example:

    .section .mydata_start, "aw"
start:
    .section .mydata_foo_type, "aw"
    .section .mydata_bar_type, "aw"
    .section .mydata_baz_type, "aw"
    .section .mydata_end, "aw"
end_loc:

    .macro gen_data, type
    .ifndef .mydata_\()\type\()_type
    .error  "gen_data invoked with unknown type '\type\()'"
    .endif
    .pushsection .mydata_\()\type\()_type, "aw"
    .word   1
    .popsection
    .endm

    .text
    gen_data foo
    gen_data bar
    gen_data baz
    # gen_data unk

    .section .head, "aw"
    .quad   start
    .quad   end_loc

If you have multiple assembler files using the macro make sure that all the assembler files include the .section directives shown above at the start so it doesn't matter which order they appear on the linker command line.

Note that both solutions resolve a problem with your macro, it's possible other unknown sections can be placed in between your .mydata_XXX_type sections if they happen to appear to the linker for the first time in that order.