This is an archive of the discontinued LLVM Phabricator instance.

[lld] Synthesize metadata for MTE globals
ClosedPublic

Authored by hctim on Jun 14 2023, 7:37 AM.

Details

Summary

As per the ABI at
https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst,
this patch interprets the SHT_AARCH64_MEMTAG_GLOBALS_STATIC section,
which contains R_NONE relocations to tagged globals, and emits a
SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC section, with the correct
DT_AARCH64_MEMTAG_GLOBALS and DT_AARCH64_MEMTAG_GLOBALSSZ dynamic
entries. This section describes, in a uleb-encoded stream, global memory
ranges that should be tagged with MTE.

We are also out of bits to spare in the LLD Symbol class. As a result,
I've reused the 'needsTocRestore' bit, which is a PPC64 only feature.
Now, it's also used for 'isTagged' on AArch64.

An entry in SHT_AARCH64_MEMTAG_GLOBALS_STATIC is practically a guarantee
from an objfile that all references to the linked symbol are through the
GOT, and meet correct alignment requirements. As a result, we go through
all symbols and make sure that, for all symbols $SYM, all object files
that reference $SYM also have a SHT_AARCH64_MEMTAG_GLOBALS_STATIC entry
for $SYM. If this isn't the case, we demote the symbol to being
untagged. Symbols that are imported from other DSOs should always be
fine, as they're GOT-referenced (and thus the GOT entry either has the
correct tag or not, depending on whether it's tagged in the defining DSO
or not).

Additionally hand-tested by building {libc, libm, libc++, libm, and libnetd}
on Android with some experimental MTE globals support in the
linker/libc.

Diff Detail

Event Timeline

hctim created this revision.Jun 14 2023, 7:37 AM
Herald added a project: Restricted Project. · View Herald Transcript
hctim requested review of this revision.Jun 14 2023, 7:37 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 14 2023, 7:37 AM

I'll try and take a look over the next few days. Will need to reload my brain with the MTE spec first.

Just a few small comments so far. I've got up to Relocations.cpp. Hope to carry on later this week.

lld/ELF/Arch/AArch64.cpp
404

A small nit. LLD tends to prefer no curly brackets when there is only one statement.

673

IIUC this will change an ADRP, ADD into an ADR, which is just a shorter ranged ADRP/ADD. This relaxation doesn't change the destination address like tryRelaxAdrpLdr might.

.

787

To help prevent future relaxations from not considering MTE, It may be worth moving the sym.isTagged() to here with a general condition similar to needsGot in relocations.cpp.

For example don't relax if the symbol is tagged and is relative to a GOT reference.

Alternatively could be worth a comment in the Relaxer class to highlight the restriction.

lld/ELF/Driver.cpp
386

Not sure what this difference is from. If it has come from clang-format it may be worth NFC committing it separately to avoid it getting associated with this patch.

789

To check my understanding; this is being deleted because protecting globals is independent of heap and stack tagging.

I've gone through all of the LLD changes now.

lld/ELF/Driver.cpp
2621

I think Fuchsia uses REL dynamic relocations with -z rel , may be worth making this a user visible error message as this could happen with user input.

2957

At this stage can you use ctx.objectFiles this seems to be more idiomatic than processing the files directly.

2963

I think clang-format will prefer

if (section == nullptr)
  continue;

It is coming out all as one line on my screen, apologies if that is just Phabricator.
May want to combine the two if statements into one. Also LLD tends to use !section

if (!section || section->type != SHT_AARCH64_MEMTAG_GLOBALS_STATIC)
  continue;

Given that this is after Garbage Collection I think you'll need to check if s== &InputSection::discarded.

2970

Is it ever possible to get a case where a STB_LOCAL binding symbol definition is incorrectly tagged? For example all references to it should be within the same TU, which should share the same command line options. It is possible some function attribute might prevent this though.

If it isn't possible then you might be able to just iterate the global symbols.

2981

I'm pretty sure this isn't necessary, I've not seen any other code that checks. I guess it might be possible that a broken object file might contain a 0 index for a symbol, but if it does LLD will already have crashed by now.

lld/ELF/SyntheticSections.cpp
3909

Although not ideal either, would writeULEB128 be a better name as compute implies that it is just computing the value and not writing it?

I guess computeOrWrite is the most descriptive but possibly a bit long.

3922

Although it may not be worth dealing with, I think you could pull this out into a separate function that you could call after the first address assignment in finalizeAddressDependentContent, as by that time the relative order of the symbols would be fixed.

3950

One possibility would be to have an internal buffer that could cache the results so that writeTo would just need to write it out. I guess the problem is that we won't know how big to make it, so I guess it would need to be a self expanding buffer.

Not sure if it is worth doing, as it might end up more complicated than it is already.

3964

By caller's do you mean call-sites?

3978

At first glance I thought that the contents of the MemtagDescriptors would be stable and could be cached, but I think the uleb encoding could change depending on the addresses so it will need recalculating.

If I'm right it could be worth adding a comment.

lld/ELF/SyntheticSections.h
1257

Looks like the call site has this check before calling addSymbol. Is it worth making that an assert instead?

hctim marked 16 inline comments as done.Jun 29 2023, 6:02 AM
hctim added inline comments.
lld/ELF/Arch/AArch64.cpp
673

yep, good point, removed

787

Done, moved it to a general conditional branch.

lld/ELF/Driver.cpp
386

ack, removed from diff

789

Yeah, that's correct. We want to allow --android-memtag-mode=sync without requiring heap/stack, as we want to enable building globals-only binaries.

2970

Ooh, nice tip! I've made addTaggedSymbolReferences mark them immediately as tagged.

2981

Done. I swear I added this for a reason, but I'll rebuild Android and check.

lld/ELF/SyntheticSections.cpp
3909

let's go with computeOrWriteULEB128

3922

done (glued it up inside finalizeAddressDependentContent)

3950

now that we're not sorting on the size call (and only doing it in the finalization of the address dependent content), less worth doing anything this complicated.

3964

this is gone with the new glue around finalizeAddressDependentContent

3978

done, in the header

hctim updated this revision to Diff 535757.Jun 29 2023, 6:03 AM
hctim marked 11 inline comments as done.

Update from Peter's comments (thanks for the review!).

Thanks for the updates. The code-changes look good to me on my current understanding. I've made a few suggestions on the testing. I'm hoping to read through the spec in detail to see if I've missed anything, but I don't want to let that hold up the patch if I can't get round to it in a reasonable amount of time.

lld/test/ELF/Inputs/aarch64-memtag-globals-1.s
12 ↗(On Diff #535757)

Speaking of hidden, I'm guessing that attribute((visibility("hidden"))) might affect a variable being in the GOT in the same way as static. If it does then could be worth adding a test.

51 ↗(On Diff #535757)

The spec says that TLS variables are not tagged (although that could change), is it worth adding a case for TLS.

lld/test/ELF/aarch64-memtag-globals.s
5

One thing that might be worth doing is using split-file (see aarch64-thunk-pi.s for an example ) to include instructions to create aarch64-memtag-globals-1.s and memtag-globals-2.s in the same source file. There's a chance it makes it too big to be worth the effort, but could be worth an expirement to see if it is easier to read than splitting across several files.

11

typo in relocatiosn

17

Worth dumping the dynamic tags to check for DT_AARCH64_MEMTAG_GLOBALS and DT_AARCH64_MEMTAG_GLOBALSSZ ?

Also could be worth dumping section headers to check for SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC

hctim marked 5 inline comments as done.Jul 7 2023, 2:27 AM
hctim added inline comments.
lld/test/ELF/aarch64-memtag-globals.s
17

The dynamic entries are dumped by --memtag (see the checks near "CHECK: Memtag Dynamic Entries").

For SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC, done.

hctim updated this revision to Diff 538042.Jul 7 2023, 2:27 AM
hctim marked an inline comment as done.

Update from Peter's test comments.

Thanks for the updates. One query about the R_AARCH64_PREL64 relocation as it looks like that needs to be separated out, but otherwise looks good to me. I'll be happy to approve, although I think it would be good to get MaskRay's approval as well.

lld/ELF/Arch/AArch64.cpp
395

Does R_AARCH64_PREL64 need this behaviour? It isn't a dynamic relocation, won't generate a R_AARCH64_RELATIVE and calculates an offset to symbol from the place. I can't see it in the spec either.

Does it need to be?

case R_AARCH64_PREL64:
  write64(loc, val);
  break;
case R_AARCH64_ABS64:
  // As below

Just for clarification, an R_AARCH64_ABS64 static relocation can be either converted to a relative or can be a dynamic reloc itself

extern int foo;
int *bar = &foo;

Is it OK to encode the addend as for R_AARCH64_RELATIVE in both cases?

hctim updated this revision to Diff 538552.Jul 10 2023, 2:32 AM
hctim marked an inline comment as done.

Update from comments.

hctim added inline comments.Jul 10 2023, 2:32 AM
lld/ELF/Arch/AArch64.cpp
395

Done, removed it from the PREL64 branch. Yes, the encoded addend is intended and desirable for RELATIVE as well. It handles the case when we have an out-of-bounds (e.g. 'endptr') pointer to a local global variable.

peter.smith accepted this revision.Jul 10 2023, 5:46 AM

Thanks for the updates. This looks good to me. Will be good to wait for a few days to see if MaskRay has any comments or objections.

This revision is now accepted and ready to land.Jul 10 2023, 5:46 AM

I'll read this shortly...

lld/test/ELF/aarch64-memtag-globals.s
4

We typically rm -rf %t the directory, especially for a large test with many temporary files. Otherwise, if we remove an output file, the test may sometimes still pass as there is a lingering file in our build directory.

19

For newer tests, we align the value of CHECK to look better

# CHECK:      check
# CHECK-NEXT: no indentation
# CHECK-NEXT:   2-space indentation
20

Ignoring a decimal number prefers [[#]]

22

[0-9a-f]+ can be replaced with [[#%x,xxx:]], though the references need to look like [[#xxx]]

MaskRay added inline comments.Jul 11 2023, 10:50 AM
lld/ELF/SyntheticSections.cpp
3962

If we have aliases, std::sort will exhibit different behaviors for different std::sort implementations or libc++ with https://libcxx.llvm.org/DesignDocs/UnspecifiedBehaviorRandomization.html
Consider stable_sort

lld/ELF/SyntheticSections.h
1250

final

lld/test/ELF/Inputs/aarch64-memtag-globals.s
384

no trailing blank line

lld/test/ELF/aarch64-memtag-globals.s
16

FileCheck %s < %t.out

MaskRay added inline comments.Jul 11 2023, 3:36 PM
lld/ELF/Arch/AArch64.cpp
391

Just use the unary operator.

lld/ELF/Driver.cpp
2613

the lambda is only used once. just expand it.

2620

TLS blocks => TLS symbols

2629

LLVM coding style prefers pre-increments

2950

We probably should move the whole function and referenced function template addTaggedSymbolReferences to AArch64.cpp.

2958

Avoid referencing an individual user. When needed (actually very rarely done for lld), reference a public issue link.

In this case, bitcode files should already work. This place is after compileBitcodeFiles, so `ctx.objectFiles contains all relocatable object files (including extracted lazy object files) and compiled bitcode files.

lld/ELF/SyntheticSections.cpp
1456

This is guaranteed to be non-null.

3926

We typically just use addr instead of vAddr.

3937

When buf is null, addr is 0. If size is 0, we'd report an error without knowing the actual address. We should drop the address from the diagnostic.

We need a --pack-dyn-relocs=relr test to show that we don't use RELR for a relative relocation referencing a tagged symbol.

This test should also check that a GOT-generating relocation referencing a non-preemptible memtag symbol does not use .relr.dyn (see local-got-shared.s).

MaskRay added 1 blocking reviewer(s): MaskRay.Jul 11 2023, 3:51 PM
This revision now requires review to proceed.Jul 11 2023, 3:51 PM
hctim updated this revision to Diff 539549.Jul 12 2023, 7:16 AM
hctim marked 17 inline comments as done.

Update w/ Ray's comments.

lld/ELF/Arch/AArch64.cpp
391

Unfortunately not possible here. e.g. ~17 != -1 * 17. While we could change the ABI to be granules-away (and then do a % 16 on the offset at the end of the calculation), giving an exact offset in bytes gives a nice easy way to debug the exact address of the tag derivation (and doesn't waste any bytes doing so).

lld/ELF/Driver.cpp
2958

Thanks for the tip!

lld/ELF/SyntheticSections.cpp
1456

isNeeded() is even better :).

lld/test/ELF/Inputs/aarch64-memtag-globals.s
384

assuming you mean no double-trailing-newline, done

lld/test/ELF/aarch64-memtag-globals.s
20

Yep, for ignoring sizes, done.

For ignoring relocation types, changed to {{[0-9a-f]+}} which to me is more understandable than [[#%x,]]`.

MaskRay added inline comments.Jul 12 2023, 6:28 PM
lld/ELF/Arch/AArch64.cpp
391

Sorry for being clear. I mean write64(loc, -rel.addend);.

We use unary operator elsewhere. It's much more common than -1 * xxx

hctim updated this revision to Diff 539869.Jul 13 2023, 12:35 AM
hctim marked an inline comment as done.

Move to '-' from '-1 *'

Thank you for the updates and sorry for the delayed response. I took a look again.

You can ping me more frequently:) I am going to take a trip and will likely have slow response for ~2 weeks since Jul 27.

lld/ELF/Arch/AArch64.cpp
388

rel.sym is guaranteed to be non-null. The nullness check can be removed

389

Consider using a single unsigned comparison and omitting the zero comparison.

985

This assert is unneeded now that this code lives in AArch64.cpp

986

We use const references to assert non-nullness.

990

This error is untested. We can use yaml2obj to craft a test.

1033

A BinaryFile has just one .data section. There can't be SHT_AARCH64_MEMTAG_GLOBALS_STATIC sections.

1067

llvm style prefers pre-increments.

lld/ELF/Relocations.cpp
866

case R_AARCH64_ABS64: in AArch64.cpp uses the signed int64_t, different from here.

Perhaps we can just omit the < 0 comparison and rely on unsigned comparison? If int64_t(addend) < 0, uint64_t(addend) will be very large.

Then adjust the comment to

// For tagged symbols with addends outside of [0, sym.getSize()), ...

lld/ELF/SyntheticSections.h
533

This change is untested. We seem to need a --apply-dynamic-relocs test inspecting the relocated data with llvm-readelf -x or llvm-objdump -s.

1255

clang-format will remove the space

1265

For new code, we use const reference to assert non-nullness.

1270

Why is the !config->isStatic && condition?

isConfig can be toggled by -Bstatic/-static and -Bdynamic.
-Bstatic can be used with a dynamically linked executable as well.

1274

Nit: SmallVector<const Symbol *, 0> is likely more efficient due to smaller size :)

1310

Place this after memtagAndroidNote for an alphabetical order

lld/test/ELF/aarch64-memtag-globals.s
88

Add -NEXT whenever appropriate.

91

Do you mean this?

99

Perhaps:

# CHECK:      Memtag Global Descriptors:
# CHECK-NEXT:   0x[[#POINTER_TO_GLOBAL]]: 0x10
# CHECK-NEXT:   0x[[#POINTER_INSIDE_GLOBAL]]: 0x10
...
# CHECK-NOT:  {{.}}

We already rely on the order, so we can just use -NEXT: patterns to avoid CHECK-NOT:

hctim updated this revision to Diff 543560.Jul 24 2023, 8:09 AM
hctim marked 16 inline comments as done.

Update with comments from Ray.

lld/ELF/Arch/AArch64.cpp
388

Surprisingly, this causes failures in other tests if removed:

lld :: ELF/aarch64-call26-thunk.s
lld :: ELF/aarch64-cortex-a53-843419-large.s
lld :: ELF/aarch64-cortex-a53-843419-thunk.s
lld :: ELF/aarch64-jump26-thunk.s
lld :: ELF/aarch64-range-thunk-extension-plt32.s

For now, leaving as-is.

389

(see same comment below about unsigned comparison)

986

Made the parameter byref, assuming you also mean the same about non-const refs.

lld/ELF/Relocations.cpp
866

I've gone ahead and fleshed out the comment, but I really don't think that relying on "signed integer when converted to unsigned is so large as to be out of range" is a great idea. While it may reduce one arithmetic operation, it decreases readability as the intent is no longer evident from the code (no matter how well it's described in the comments).

lld/ELF/SyntheticSections.h
533

Removed this condition here, and banned --apply-dynamic-relocs when using MTE globals.

We could make it so --apply-dynamic-relocs works in all the places where it *can*, but this seems really prone to confusion ("why do I have the correct relocation here but not there? Oh, it's because of the MTE globals relocs!" isn't completely obvious).

1270

MTE globals are not supported for fully static executables, there needs to be a dynamic loader that processes relocations.

Is there a better way to describe this in code? Maybe pulling needsInterpSection out of Writer.cpp?

lld/test/ELF/aarch64-memtag-globals.s
91

Yeah, ish. [0-9a-f]+ is fine though.

MaskRay accepted this revision.Jul 24 2023, 12:38 PM

Thanks!

lld/ELF/Arch/AArch64.cpp
766

rel.sym && can be removed. relocateAlloc guarantees that rel.sym != nullptr.

Sorry for my suggestion in another place. R_AARCH64_ABS64 does need a rel.sym check as it is used by relocateNoSym for thunks and relocateNonAlloc. I have done D73254 in the past to clean it a bit. I will do more cleanup so that
R_AARCH64_ABS64 can remove the rel.sym check as well.

1048

Remove this comment. As mentioned, LTO is already supported.

lld/ELF/SyntheticSections.cpp
3923
3930

Consider errorOrWarn so that --noinhibit-exec links can produce output even with errors.

I'm fine with no testing if this is difficult to do.

3932

no trailing full stop

lld/ELF/Writer.h
59

Below addReservedSymbols may be a good place. The last few declarations are for mips.

This revision is now accepted and ready to land.Jul 24 2023, 12:38 PM
hctim marked 6 inline comments as done.Jul 25 2023, 7:42 AM
hctim added inline comments.
lld/ELF/SyntheticSections.cpp
3923

Added the type, but it's actually const Symbol* as the container needs to store pointers to allow sorting.

lld/ELF/SyntheticSections.h
1270

@MaskRay - any suggestions here?

hctim updated this revision to Diff 543972.Jul 25 2023, 7:42 AM
hctim marked an inline comment as done.

More comment updates.

MaskRay accepted this revision.Jul 26 2023, 10:44 AM

LGTM again. Feel free to file a request to merge this into https://github.com/llvm/llvm-project/tree/release/17.x if you prefer.

lld/ELF/SyntheticSections.h
1270

Yes, moving needsInterpSection to Writer.h and exposing it here looks good to me.

You will need to move isNeeded to SyntheticSections.cpp as we cannot make SyntheticSections.h include Writer.h.

Ideally SyntheticSections.cpp does not use more functions from Writer.h, so SyntheticSections.cpp defining needsInterpSection is also fine.

hctim marked 2 inline comments as done.Jul 31 2023, 7:52 AM
hctim added inline comments.
lld/ELF/SyntheticSections.h
1270

Yeah, so it needed a little bit more precise of a check, which I put straight in the relevant bit of Writer.cpp (so it doesn't need to be exported).

We don't allow MTE globals for fully-static binaries, as the loader needs to do some work. I added a test with --static (and nothing else, as this is what clang -static seems to emit, along with ctrbegin/end as static) as well. We basically error-out when you try and --android-memtag-mode on a non-DSO non-partially-linked (--relocatable) non-interpreted binary.

hctim updated this revision to Diff 545667.Jul 31 2023, 7:52 AM
hctim marked an inline comment as done.

Final updates.

This revision was landed with ongoing or failed builds.Jul 31 2023, 8:08 AM
This revision was automatically updated to reflect the committed changes.