Merge changes from topic "revert-2709995-VVPYYBKIHY" into main am: 73a867a203 am: 3f54874625

Original change: https://android-review.googlesource.com/c/platform/bionic/+/2862867

Change-Id: If4a04505e815cc7558f02d9741feb3e99e983225
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Evgenii Stepanov 2023-12-07 03:05:38 +00:00 committed by Automerger Merge Worker
commit 76a20d12f3
22 changed files with 20 additions and 870 deletions

View file

@ -44,7 +44,7 @@ extern "C" int __set_tid_address(int* tid_address);
// Declared in "private/bionic_ssp.h".
uintptr_t __stack_chk_guard = 0;
BIONIC_USED_BEFORE_LINKER_RELOCATES static pthread_internal_t main_thread;
static pthread_internal_t main_thread;
// Setup for the main thread. For dynamic executables, this is called by the
// linker _before_ libc is mapped in memory. This means that all writes to

View file

@ -31,7 +31,6 @@
#include <sys/hwprobe.h>
#include <sys/ifunc.h>
#include "bionic/macros.h"
#include "private/bionic_auxv.h"
// This code is called in the linker before it has been relocated, so minimize calls into other
@ -41,8 +40,8 @@
ElfW(Addr) __bionic_call_ifunc_resolver(ElfW(Addr) resolver_addr) {
#if defined(__aarch64__)
typedef ElfW(Addr) (*ifunc_resolver_t)(uint64_t, __ifunc_arg_t*);
BIONIC_USED_BEFORE_LINKER_RELOCATES static __ifunc_arg_t arg;
BIONIC_USED_BEFORE_LINKER_RELOCATES static bool initialized = false;
static __ifunc_arg_t arg;
static bool initialized = false;
if (!initialized) {
initialized = true;
arg._size = sizeof(__ifunc_arg_t);

View file

@ -410,7 +410,6 @@ __attribute__((no_sanitize("hwaddress", "memtag"))) void __libc_init_mte(
// We did not enable MTE, so we do not need to arm the upgrade timer.
__libc_shared_globals()->heap_tagging_upgrade_timer_sec = 0;
}
#else // __aarch64__
void __libc_init_mte(const memtag_dynamic_entries_t*, const void*, size_t, uintptr_t, void*) {}
#endif // __aarch64__
@ -505,6 +504,6 @@ extern "C" void android_set_application_target_sdk_version(int target) {
// compiled with -ffreestanding to avoid implicit string.h function calls. (It shouldn't strictly
// be necessary, though.)
__LIBC_HIDDEN__ libc_shared_globals* __libc_shared_globals() {
BIONIC_USED_BEFORE_LINKER_RELOCATES static libc_shared_globals globals;
static libc_shared_globals globals;
return &globals;
}

View file

@ -97,17 +97,3 @@ template <typename T>
static inline T* _Nonnull untag_address(T* _Nonnull p) {
return reinterpret_cast<T*>(untag_address(reinterpret_cast<uintptr_t>(p)));
}
// MTE globals protects internal and external global variables. One of the main
// things that MTE globals does is force all global variables accesses to go
// through the GOT. In the linker though, some global variables are accessed (or
// address-taken) prior to relocations being processed. Because relocations
// haven't run yet, the GOT entry hasn't been populated, and this leads to
// crashes. Thus, any globals used by the linker prior to relocation should be
// annotated with this attribute, which suppresses tagging of this global
// variable, restoring the pc-relative address computation.
#if __has_feature(memtag_globals)
#define BIONIC_USED_BEFORE_LINKER_RELOCATES __attribute__((no_sanitize("memtag")))
#else // __has_feature(memtag_globals)
#define BIONIC_USED_BEFORE_LINKER_RELOCATES
#endif // __has_feature(memtag_globals)

View file

@ -28,7 +28,6 @@
#pragma once
#include <stddef.h>
#include <sys/auxv.h>
#include <sys/prctl.h>
@ -47,36 +46,6 @@ inline bool mte_supported() {
return supported;
}
inline void* get_tagged_address(const void* ptr) {
#if defined(__aarch64__)
if (mte_supported()) {
__asm__ __volatile__(".arch_extension mte; ldg %0, [%0]" : "+r"(ptr));
}
#endif // aarch64
return const_cast<void*>(ptr);
}
// Inserts a random tag tag to `ptr`, using any of the set lower 16 bits in
// `mask` to exclude the corresponding tag from being generated. Note: This does
// not tag memory.
inline void* insert_random_tag(const void* ptr, __attribute__((unused)) uint64_t mask = 0) {
#if defined(__aarch64__)
if (mte_supported() && ptr) {
__asm__ __volatile__(".arch_extension mte; irg %0, %0, %1" : "+r"(ptr) : "r"(mask));
}
#endif // aarch64
return const_cast<void*>(ptr);
}
// Stores the address tag in `ptr` to memory, at `ptr`.
inline void set_memory_tag(__attribute__((unused)) void* ptr) {
#if defined(__aarch64__)
if (mte_supported()) {
__asm__ __volatile__(".arch_extension mte; stg %0, [%0]" : "+r"(ptr));
}
#endif // aarch64
}
#ifdef __aarch64__
class ScopedDisableMTE {
size_t prev_tco_;

View file

@ -328,10 +328,6 @@ cc_defaults {
sanitize: {
hwaddress: false,
// TODO(mitchp): For now, disable MTE globals in the linker. MTE globals
// change the relocation semantics, and GVs that are touched before the
// linker has the chance to relocate itself have to be annotated.
memtag_globals: false,
},
static_libs: [

View file

@ -331,7 +331,6 @@ soinfo* get_libdl_info(const soinfo& linker_si) {
__libdl_info->gnu_bloom_filter_ = linker_si.gnu_bloom_filter_;
__libdl_info->gnu_bucket_ = linker_si.gnu_bucket_;
__libdl_info->gnu_chain_ = linker_si.gnu_chain_;
__libdl_info->memtag_dynamic_entries_ = linker_si.memtag_dynamic_entries_;
__libdl_info->ref_count_ = 1;
__libdl_info->strtab_size_ = linker_si.strtab_size_;

View file

@ -51,7 +51,6 @@
#include <android-base/scopeguard.h>
#include <async_safe/log.h>
#include <bionic/pthread_internal.h>
#include <platform/bionic/mte.h>
// Private C library headers.
@ -2340,7 +2339,7 @@ bool do_dlsym(void* handle,
void* tls_block = get_tls_block_for_this_thread(tls_module, /*should_alloc=*/true);
*symbol = static_cast<char*>(tls_block) + sym->st_value;
} else {
*symbol = get_tagged_address(reinterpret_cast<void*>(found->resolve_symbol_address(sym)));
*symbol = reinterpret_cast<void*>(found->resolve_symbol_address(sym));
}
failure_guard.Disable();
LD_LOG(kLogDlsym,
@ -2771,11 +2770,8 @@ bool soinfo::lookup_version_info(const VersionTracker& version_tracker, ElfW(Wor
}
void soinfo::apply_relr_reloc(ElfW(Addr) offset) {
ElfW(Addr)* tagged_address = reinterpret_cast<ElfW(Addr)*>(
get_tagged_address(reinterpret_cast<void*>(offset + load_bias)));
ElfW(Addr) tagged_result = reinterpret_cast<ElfW(Addr)>(
get_tagged_address(reinterpret_cast<void*>(*tagged_address + load_bias)));
*tagged_address = tagged_result;
ElfW(Addr) address = offset + load_bias;
*reinterpret_cast<ElfW(Addr)*>(address) += load_bias;
}
// Process relocations in SHT_RELR section (experimental).
@ -3308,18 +3304,6 @@ bool soinfo::prelink_image() {
// it each time we look up a symbol with a version.
if (!validate_verdef_section(this)) return false;
// MTE globals requires remapping data segments with PROT_MTE as anonymous mappings, because file
// based mappings may not be backed by tag-capable memory (see "MAP_ANONYMOUS" on
// https://www.kernel.org/doc/html/latest/arch/arm64/memory-tagging-extension.html). This is only
// done if the binary has MTE globals (evidenced by the dynamic table entries), as it destroys
// page sharing. It's also only done on devices that support MTE, because the act of remapping
// pages is unnecessary on non-MTE devices (where we might still run MTE-globals enabled code).
if (mte_supported() && memtag_globals() && memtag_globalssz() &&
remap_memtag_globals_segments(phdr, phnum, base) == 0) {
tag_globals();
protect_memtag_globals_ro_segments(phdr, phnum, base);
}
flags_ |= FLAG_PRELINKED;
return true;
}
@ -3391,14 +3375,6 @@ bool soinfo::link_image(const SymbolLookupList& lookup_list, soinfo* local_group
return false;
}
if (mte_supported() && memtag_globals() && memtag_globalssz()) {
// The linker's full path is not available until the main executable is loaded, as it's obtained
// from DT_INTERP. We manually rename the linker's segments later, but have a best-effort name
// in case we find a bug prior to loading the main executable.
const char* soname = is_linker() ? "linker" : get_realpath();
name_memtag_globals_segments(phdr, phnum, base, soname);
}
/* Handle serializing/sharing the RELRO segment */
if (extinfo && (extinfo->flags & ANDROID_DLEXT_WRITE_RELRO)) {
if (phdr_table_serialize_gnu_relro(phdr, phnum, load_bias,
@ -3431,47 +3407,6 @@ bool soinfo::protect_relro() {
return true;
}
// https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst#global-variable-tagging
void soinfo::tag_globals() {
if (is_linked()) return;
if (flags_ & FLAG_GLOBALS_TAGGED) return;
flags_ |= FLAG_GLOBALS_TAGGED;
constexpr size_t kTagGranuleSize = 16;
const uint8_t* descriptor_stream = reinterpret_cast<const uint8_t*>(memtag_globals());
if (memtag_globalssz() == 0) {
DL_ERR("Invalid memtag descriptor pool size: %zu", memtag_globalssz());
}
uint64_t addr = 0;
uleb128_decoder decoder(descriptor_stream, memtag_globalssz());
// Don't ever generate tag zero, to easily distinguish between tagged and
// untagged globals in register/tag dumps.
uint64_t last_tag_mask = 1;
constexpr uint64_t kMemtagStepVarintReservedBits = 3;
while (decoder.has_bytes()) {
uint64_t value = decoder.pop_front();
uint64_t step = value >> kMemtagStepVarintReservedBits;
uint64_t granules_to_tag = value & ((1 << kMemtagStepVarintReservedBits) - 1);
if (granules_to_tag == 0) {
granules_to_tag = decoder.pop_front() + 1;
}
addr += step * kTagGranuleSize;
void* tagged_addr = insert_random_tag(reinterpret_cast<void*>(addr + load_bias), last_tag_mask);
uint64_t tag = (reinterpret_cast<uint64_t>(tagged_addr) >> 56) & 0x0f;
last_tag_mask = 1 | (1 << tag);
for (size_t k = 0; k < granules_to_tag; k++) {
auto* granule = static_cast<uint8_t*>(tagged_addr) + k * kTagGranuleSize;
set_memory_tag(static_cast<void*>(granule));
}
addr += granules_to_tag * kTagGranuleSize;
}
}
static std::vector<android_namespace_t*> init_default_namespace_no_config(bool is_asan, bool is_hwasan) {
g_default_namespace.set_isolated(false);
auto default_ld_paths = is_asan ? kAsanDefaultLdPaths : (

View file

@ -44,9 +44,7 @@
#include "linker_tls.h"
#include "linker_utils.h"
#include "platform/bionic/macros.h"
#include "private/KernelArgumentBlock.h"
#include "private/bionic_auxv.h"
#include "private/bionic_call_ifunc_resolver.h"
#include "private/bionic_globals.h"
#include "private/bionic_tls.h"
@ -80,8 +78,7 @@ void __libc_init_mte(const memtag_dynamic_entries_t* memtag_dynamic_entries, con
// TODO (dimtiry): remove somain, rename solist to solist_head
static soinfo* solist;
static soinfo* sonext;
// main process, always the one after libdl_info
BIONIC_USED_BEFORE_LINKER_RELOCATES static soinfo* somain;
static soinfo* somain; // main process, always the one after libdl_info
static soinfo* solinker;
static soinfo* vdso; // vdso if present
@ -393,16 +390,9 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load
interp = kFallbackLinkerPath;
}
solinker->set_realpath(interp);
if (solinker->memtag_globals() && solinker->memtag_globalssz()) {
name_memtag_globals_segments(solinker->phdr, solinker->phnum, solinker->load_bias,
solinker->get_realpath());
}
init_link_map_head(*solinker);
#if defined(__aarch64__)
__libc_init_mte(somain->memtag_dynamic_entries(), somain->phdr, somain->phnum, somain->load_bias,
args.argv);
if (exe_to_load == nullptr) {
// Kernel does not add PROT_BTI to executable pages of the loaded ELF.
// Apply appropriate protections here if it is needed.
@ -414,6 +404,9 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load
strerror(errno));
}
}
__libc_init_mte(somain->memtag_dynamic_entries(), somain->phdr, somain->phnum, somain->load_bias,
args.argv);
#endif
// Register the main executable and the linker upfront to have
@ -612,7 +605,7 @@ const unsigned kRelTag = DT_REL;
const unsigned kRelSzTag = DT_RELSZ;
#endif
BIONIC_USED_BEFORE_LINKER_RELOCATES extern __LIBC_HIDDEN__ ElfW(Ehdr) __ehdr_start;
extern __LIBC_HIDDEN__ ElfW(Ehdr) __ehdr_start;
static void call_ifunc_resolvers_for_section(RelType* begin, RelType* end) {
auto ehdr = reinterpret_cast<ElfW(Addr)>(&__ehdr_start);
@ -670,16 +663,6 @@ static void linker_memclr(void* dst, size_t cnt) {
}
}
// Remapping MTE globals segments happens before the linker relocates itself, and so can't use
// memcpy() from string.h. This function is compiled with -ffreestanding.
void linker_memcpy(void* dest, const void* src, size_t n) {
char* dest_bytes = reinterpret_cast<char*>(dest);
const char* src_bytes = reinterpret_cast<const char*>(src);
for (size_t i = 0; i < n; ++i) {
dest_bytes[i] = src_bytes[i];
}
}
// Detect an attempt to run the linker on itself. e.g.:
// /system/bin/linker64 /system/bin/linker64
// Use priority-1 to run this constructor before other constructors.

View file

@ -70,5 +70,3 @@ bool solist_remove_soinfo(soinfo* si);
soinfo* solist_get_head();
soinfo* solist_get_somain();
soinfo* solist_get_vdso();
void linker_memcpy(void* dest, const void* src, size_t n);

View file

@ -36,12 +36,10 @@
#include <sys/stat.h>
#include <unistd.h>
#include "android-base/unique_fd.h"
#include "linker.h"
#include "linker_debug.h"
#include "linker_dlwarning.h"
#include "linker_globals.h"
#include "linker_main.h"
#include "linker_debug.h"
#include "linker_utils.h"
#include "private/CFIShadow.h" // For kLibraryAlignment
@ -864,108 +862,6 @@ int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, prot);
}
static bool segment_needs_memtag_globals_remapping(const ElfW(Phdr) * phdr) {
// For now, MTE globals is only supported on writeable data segments.
return phdr->p_type == PT_LOAD && !(phdr->p_flags & PF_X) && (phdr->p_flags & PF_W);
}
/* When MTE globals are requested by the binary, and when the hardware supports
* it, remap the executable's PT_LOAD data pages to have PROT_MTE.
*
* Input:
* phdr_table -> program header table
* phdr_count -> number of entries in tables
* load_bias -> load bias
* Return:
* 0 on success, -1 on failure (error code in errno).
*/
int remap_memtag_globals_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count,
ElfW(Addr) load_bias) {
for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_table + phdr_count; phdr++) {
if (!segment_needs_memtag_globals_remapping(phdr)) {
continue;
}
uintptr_t seg_page_start = page_start(phdr->p_vaddr) + load_bias;
uintptr_t seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
size_t seg_page_aligned_size = seg_page_end - seg_page_start;
int prot = PFLAGS_TO_PROT(phdr->p_flags);
// For anonymous private mappings, it may be possible to simply mprotect()
// the PROT_MTE flag over the top. For file-based mappings, this will fail,
// and we'll need to fall back. We also allow PROT_WRITE here to allow
// writing memory tags (in `soinfo::tag_globals()`), and set these sections
// back to read-only after tags are applied (similar to RELRO).
#if defined(__aarch64__)
prot |= PROT_MTE;
#endif // defined(__aarch64__)
if (mprotect(reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size,
prot | PROT_WRITE) == 0) {
continue;
}
void* mapping_copy = mmap(nullptr, seg_page_aligned_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
linker_memcpy(mapping_copy, reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size);
void* seg_addr = mmap(reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size,
prot | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (seg_addr == MAP_FAILED) return -1;
linker_memcpy(seg_addr, mapping_copy, seg_page_aligned_size);
munmap(mapping_copy, seg_page_aligned_size);
}
return 0;
}
void protect_memtag_globals_ro_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count,
ElfW(Addr) load_bias) {
for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_table + phdr_count; phdr++) {
int prot = PFLAGS_TO_PROT(phdr->p_flags);
if (!segment_needs_memtag_globals_remapping(phdr) || (prot & PROT_WRITE)) {
continue;
}
#if defined(__aarch64__)
prot |= PROT_MTE;
#endif // defined(__aarch64__)
uintptr_t seg_page_start = page_start(phdr->p_vaddr) + load_bias;
uintptr_t seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
size_t seg_page_aligned_size = seg_page_end - seg_page_start;
mprotect(reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size, prot);
}
}
void name_memtag_globals_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count,
ElfW(Addr) load_bias, const char* soname) {
for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_table + phdr_count; phdr++) {
if (!segment_needs_memtag_globals_remapping(phdr)) {
continue;
}
uintptr_t seg_page_start = page_start(phdr->p_vaddr) + load_bias;
uintptr_t seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
size_t seg_page_aligned_size = seg_page_end - seg_page_start;
// For file-based mappings that we're now forcing to be anonymous mappings, set the VMA name to
// make debugging easier. The previous Android-kernel specific implementation captured the name
// by pointer from userspace, which meant we had to persist the name permanently in memory.
// Since Android13-5.10 (https://android-review.git.corp.google.com/c/kernel/common/+/1934723)
// though, we use the upstream-kernel implementation
// (https://github.com/torvalds/linux/commit/9a10064f5625d5572c3626c1516e0bebc6c9fe9b), which
// copies the name into kernel memory. It's a safe bet that any devices with Android 14 are
// using a kernel >= 5.10.
constexpr unsigned kVmaNameLimit = 80;
char vma_name[kVmaNameLimit];
async_safe_format_buffer(vma_name, kVmaNameLimit, "memtag:%s+0x%" PRIxPTR, soname,
page_start(phdr->p_vaddr));
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<void*>(seg_page_start),
seg_page_aligned_size, vma_name);
}
}
/* Change the protection of all loaded segments in memory to writable.
* This is useful before performing relocations. Once completed, you
* will have to call phdr_table_protect_segments to restore the original

View file

@ -148,12 +148,3 @@ void phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_co
const char* phdr_table_get_interpreter_name(const ElfW(Phdr)* phdr_table, size_t phdr_count,
ElfW(Addr) load_bias);
int remap_memtag_globals_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count,
ElfW(Addr) load_bias);
void protect_memtag_globals_ro_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count,
ElfW(Addr) load_bias);
void name_memtag_globals_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count,
ElfW(Addr) load_bias, const char* soname);

View file

@ -44,8 +44,6 @@
#include "linker_soinfo.h"
#include "private/bionic_globals.h"
#include <platform/bionic/mte.h>
static bool is_tls_reloc(ElfW(Word) type) {
switch (type) {
case R_GENERIC_TLS_DTPMOD:
@ -159,19 +157,12 @@ void print_linker_stats() {
static bool process_relocation_general(Relocator& relocator, const rel_t& reloc);
static ElfW(Addr) apply_memtag(ElfW(Addr) sym_addr) {
if (sym_addr == 0) return sym_addr; // Handle undefined weak symbols.
return reinterpret_cast<ElfW(Addr)>(get_tagged_address(reinterpret_cast<void*>(sym_addr)));
}
template <RelocMode Mode>
__attribute__((always_inline))
static bool process_relocation_impl(Relocator& relocator, const rel_t& reloc) {
constexpr bool IsGeneral = Mode == RelocMode::General;
void* const rel_target =
reinterpret_cast<void*>(apply_memtag(reloc.r_offset + relocator.si->load_bias));
void* const rel_target = reinterpret_cast<void*>(reloc.r_offset + relocator.si->load_bias);
const uint32_t r_type = ELFW(R_TYPE)(reloc.r_info);
const uint32_t r_sym = ELFW(R_SYM)(reloc.r_info);
@ -334,7 +325,7 @@ static bool process_relocation_impl(Relocator& relocator, const rel_t& reloc) {
// common in non-platform binaries.
if (r_type == R_GENERIC_ABSOLUTE) {
count_relocation_if<IsGeneral>(kRelocAbsolute);
const ElfW(Addr) result = apply_memtag(sym_addr) + get_addend_rel();
const ElfW(Addr) result = sym_addr + get_addend_rel();
trace_reloc("RELO ABSOLUTE %16p <- %16p %s",
rel_target, reinterpret_cast<void*>(result), sym_name);
*static_cast<ElfW(Addr)*>(rel_target) = result;
@ -344,29 +335,16 @@ static bool process_relocation_impl(Relocator& relocator, const rel_t& reloc) {
// document (IHI0044F) specifies that R_ARM_GLOB_DAT has an addend, but Bionic isn't adding
// it.
count_relocation_if<IsGeneral>(kRelocAbsolute);
ElfW(Addr) result = apply_memtag(sym_addr) + get_addend_norel();
trace_reloc("RELO GLOB_DAT %16p <- %16p %s", rel_target, reinterpret_cast<void*>(result),
sym_name);
const ElfW(Addr) result = sym_addr + get_addend_norel();
trace_reloc("RELO GLOB_DAT %16p <- %16p %s",
rel_target, reinterpret_cast<void*>(result), sym_name);
*static_cast<ElfW(Addr)*>(rel_target) = result;
return true;
} else if (r_type == R_GENERIC_RELATIVE) {
// In practice, r_sym is always zero, but if it weren't, the linker would still look up the
// referenced symbol (and abort if the symbol isn't found), even though it isn't used.
count_relocation_if<IsGeneral>(kRelocRelative);
ElfW(Addr) result;
// MTE globals reuses the place bits for additional tag-derivation metadata for
// R_AARCH64_RELATIVE relocations, which makes it incompatible with
// `-Wl,--apply-dynamic-relocs`. This is enforced by lld, however there's nothing stopping
// Android binaries (particularly prebuilts) from building with this linker flag if they're
// not built with MTE globals. Thus, don't use the new relocation semantics if this DSO
// doesn't have MTE globals.
if (relocator.si->memtag_globals() && relocator.si->memtag_globalssz()) {
int64_t* place = static_cast<int64_t*>(rel_target);
int64_t offset = *place;
result = apply_memtag(relocator.si->load_bias + get_addend_rel() + offset) - offset;
} else {
result = relocator.si->load_bias + get_addend_rel();
}
const ElfW(Addr) result = relocator.si->load_bias + get_addend_rel();
trace_reloc("RELO RELATIVE %16p <- %16p",
rel_target, reinterpret_cast<void*>(result));
*static_cast<ElfW(Addr)*>(rel_target) = result;

View file

@ -69,32 +69,3 @@ class sleb128_decoder {
const uint8_t* current_;
const uint8_t* const end_;
};
class uleb128_decoder {
public:
uleb128_decoder(const uint8_t* buffer, size_t count) : current_(buffer), end_(buffer + count) {}
uint64_t pop_front() {
uint64_t value = 0;
size_t shift = 0;
uint8_t byte;
do {
if (current_ >= end_) {
async_safe_fatal("uleb128_decoder ran out of bounds");
}
byte = *current_++;
value |= (static_cast<size_t>(byte & 127) << shift);
shift += 7;
} while (byte & 128);
return value;
}
bool has_bytes() { return current_ < end_; }
private:
const uint8_t* current_;
const uint8_t* const end_;
};

View file

@ -66,10 +66,9 @@
// soinfo is executed and this flag is
// unset.
#define FLAG_PRELINKED 0x00000400 // prelink_image has successfully processed this soinfo
#define FLAG_GLOBALS_TAGGED 0x00000800 // globals have been tagged by MTE.
#define FLAG_NEW_SOINFO 0x40000000 // new soinfo format
#define SOINFO_VERSION 7
#define SOINFO_VERSION 6
ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr);
@ -258,8 +257,6 @@ struct soinfo {
const android_dlextinfo* extinfo, size_t* relro_fd_offset);
bool protect_relro();
void tag_globals();
void add_child(soinfo* child);
void remove_all_links();

View file

@ -353,89 +353,6 @@ cc_prebuilt_test_library_shared {
},
}
cc_defaults {
name: "memtag_globals_defaults",
defaults: [
"bionic_testlib_defaults",
"bionic_targets_only"
],
cflags: [
"-Wno-array-bounds",
"-Wno-unused-variable",
],
header_libs: ["bionic_libc_platform_headers"],
sanitize: {
hwaddress: false,
memtag_heap: true,
memtag_globals: true,
diag: {
memtag_heap: true,
}
},
relative_install_path: "bionic-loader-test-libs/prebuilt-elf-files",
}
/// The following library and binary is to be used for testing the bionic linker
/// against memtag globals. While we wait for the compiler to be updated, the
/// files come as prebuilts (built using) the corresponding 'regular'
/// cc_test_library and cc_test rules below, just with a tip-of-tree version of
/// LLVM.
///
/// Updating the prebuilts can be done by installing a tip-of-tree compiler, and
/// running:
/// - m memtag_globals_binary memtag_globals_dso
/// - cp $ANDROID_PRODUCT_OUT/symbols/data/nativetest64/bionic-loader-test-libs/prebuilt-elf-files/memtag_globals_* \
/// $ANDROID_BUILD_TOP/bionic/tests/prebuilt-elf-files/arm64/
///
// cc_test_library {
// name: "memtag_globals_dso",
// defaults: [ "memtag_globals_defaults" ],
// srcs: ["prebuilt-elf-files/arm64/src/memtag_globals_dso.cpp"],
// }
//
// cc_test {
// name: "memtag_globals_binary",
// // Not actually a '.so' file, this is an executable; but the only way to
// // get soong to be able to produce a properly located and linked prebuilt
// // variant of this binary was through the cc_prebuilt_test_library_shared
// // rule, which appends '.so' to the end. Once the prebuilts are removed,
// // the suffix can be removed.
// suffix: ".so",
// defaults: [ "memtag_globals_defaults" ],
// srcs: ["prebuilt-elf-files/arm64/src/memtag_globals_binary.cpp"],
// shared_libs: [ "memtag_globals_dso" ],
// // This binary is used in the bionic-unit-tests as a data dependency, and is
// // in the same folder as memtag_globals_dso. But, the default cc_test rules
// // make this binary (when just explicitly built and shoved in
// // /data/nativetest64/) end up in a subfolder called
// // 'memtag_globals_binary'. When this happens, the explicit build fails to
// // find the DSO because the default rpath is just ${ORIGIN}, and because we
// // want this to be usable both from bionic-unit-tests and explicit builds,
// // let's just not put it in a subdirectory.
// no_named_install_directory: true,
// }
cc_prebuilt_test_library_shared {
name: "memtag_globals_dso",
defaults: [ "memtag_globals_defaults" ],
arch: {
arm64: {
srcs: ["prebuilt-elf-files/arm64/memtag_globals_dso.so"],
},
}
}
cc_prebuilt_test_library_shared {
name: "memtag_globals_binary",
defaults: [ "memtag_globals_defaults" ],
arch: {
arm64: {
srcs: ["prebuilt-elf-files/arm64/memtag_globals_binary.so"],
shared_libs: [ "memtag_globals_dso" ],
},
}
}
// -----------------------------------------------------------------------------
// All standard tests.
// -----------------------------------------------------------------------------
@ -510,7 +427,6 @@ cc_test_library {
"math_test.cpp",
"membarrier_test.cpp",
"memtag_stack_test.cpp",
"memtag_globals_test.cpp",
"mntent_test.cpp",
"mte_test.cpp",
"netdb_test.cpp",
@ -966,8 +882,6 @@ cc_defaults {
"preinit_syscall_test_helper",
"thread_exit_cb_helper",
"tls_properties_helper",
"memtag_globals_binary",
"memtag_globals_dso",
],
data_libs: [
"libatest_simple_zip",

View file

@ -1,58 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <gtest/gtest.h>
#if defined(__BIONIC__)
#include "gtest_globals.h"
#include "platform/bionic/mte.h"
#include "utils.h"
#endif
#include <sys/stat.h>
#include <unistd.h>
#include <string>
TEST(MemtagGlobalsTest, test) {
#if defined(__BIONIC__) && defined(__aarch64__)
std::string binary = GetPrebuiltElfDir() + "/memtag_globals_binary.so";
chmod(binary.c_str(), 0755);
ExecTestHelper eth;
eth.SetArgs({binary.c_str(), nullptr});
eth.Run(
[&]() {
execve(binary.c_str(), eth.GetArgs(), eth.GetEnv());
GTEST_FAIL() << "Failed to execve: " << strerror(errno) << "\n";
},
// We catch the global-buffer-overflow and crash only when MTE is
// supported.
mte_supported() ? -SIGSEGV : 0, "Assertions were passed");
#else
GTEST_SKIP() << "bionic/arm64 only";
#endif
}

View file

@ -1,43 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <utility>
#include <vector>
void check_tagged(const void* a);
void check_untagged(const void* a);
void check_matching_tags(const void* a, const void* b);
void check_eq(const void* a, const void* b);
void dso_check_assertions(bool enforce_tagged);
void dso_print_variables();
void print_variable_address(const char* name, const void* ptr);
void print_variables(const char* header,
const std::vector<std::pair<const char*, const void*>>& tagged_variables,
const std::vector<std::pair<const char*, const void*>>& untagged_variables);

View file

@ -1,195 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string>
#include <vector>
#include "memtag_globals.h"
// Adapted from the LLD test suite: lld/test/ELF/Inputs/aarch64-memtag-globals.s
/// Global variables defined here, of various semantics.
char global[30] = {};
__attribute__((no_sanitize("memtag"))) int global_untagged = 0;
const int const_global = 0;
static const int hidden_const_global = 0;
static char hidden_global[12] = {};
__attribute__((visibility("hidden"))) int hidden_attr_global = 0;
__attribute__((visibility("hidden"))) const int hidden_attr_const_global = 0;
/// Should be untagged.
__thread int tls_global;
__thread static int hidden_tls_global;
/// Tagged, from the other file.
extern int global_extern;
/// Untagged, from the other file.
extern __attribute__((no_sanitize("memtag"))) int global_extern_untagged;
/// Tagged here, but untagged in the definition found in the sister objfile
/// (explicitly).
extern int global_extern_untagged_definition_but_tagged_import;
/// ABS64 relocations. Also, forces symtab entries for local and external
/// globals.
char* pointer_to_global = &global[0];
char* pointer_inside_global = &global[17];
char* pointer_to_global_end = &global[30];
char* pointer_past_global_end = &global[48];
int* pointer_to_global_untagged = &global_untagged;
const int* pointer_to_const_global = &const_global;
/// RELATIVE relocations.
const int* pointer_to_hidden_const_global = &hidden_const_global;
char* pointer_to_hidden_global = &hidden_global[0];
int* pointer_to_hidden_attr_global = &hidden_attr_global;
const int* pointer_to_hidden_attr_const_global = &hidden_attr_const_global;
/// RELATIVE relocations with special AArch64 MemtagABI semantics, with the
/// offset ('12' or '16') encoded in the place.
char* pointer_to_hidden_global_end = &hidden_global[12];
char* pointer_past_hidden_global_end = &hidden_global[16];
/// ABS64 relocations.
int* pointer_to_global_extern = &global_extern;
int* pointer_to_global_extern_untagged = &global_extern_untagged;
int* pointer_to_global_extern_untagged_definition_but_tagged_import =
&global_extern_untagged_definition_but_tagged_import;
// Force materialization of these globals into the symtab.
int* get_address_to_tls_global() {
return &tls_global;
}
int* get_address_to_hidden_tls_global() {
return &hidden_tls_global;
}
static const std::vector<std::pair<const char*, const void*>>& get_expected_tagged_vars() {
static std::vector<std::pair<const char*, const void*>> expected_tagged_vars = {
{"global", &global},
{"pointer_inside_global", pointer_inside_global},
{"pointer_to_global_end", pointer_to_global_end},
{"pointer_past_global_end", pointer_past_global_end},
{"hidden_global", &hidden_global},
{"hidden_attr_global", &hidden_attr_global},
{"global_extern", &global_extern},
};
return expected_tagged_vars;
}
static const std::vector<std::pair<const char*, const void*>>& get_expected_untagged_vars() {
static std::vector<std::pair<const char*, const void*>> expected_untagged_vars = {
{"global_extern_untagged", &global_extern_untagged},
{"global_extern_untagged_definition_but_tagged_import",
&global_extern_untagged_definition_but_tagged_import},
{"global_untagged", &global_untagged},
{"const_global", &const_global},
{"hidden_const_global", &hidden_const_global},
{"hidden_attr_const_global", &hidden_attr_const_global},
{"tls_global", &tls_global},
{"hidden_tls_global", &hidden_tls_global},
};
return expected_untagged_vars;
}
void exe_print_variables() {
print_variables(" Variables accessible from the binary:\n", get_expected_tagged_vars(),
get_expected_untagged_vars());
}
// Dump the addresses of the global variables to stderr
void dso_print();
void dso_print_others();
void exe_check_assertions(bool check_pointers_are_tagged) {
// Check that non-const variables are writeable.
*pointer_to_global = 0;
*pointer_inside_global = 0;
*(pointer_to_global_end - 1) = 0;
*pointer_to_global_untagged = 0;
*pointer_to_hidden_global = 0;
*pointer_to_hidden_attr_global = 0;
*(pointer_to_hidden_global_end - 1) = 0;
*pointer_to_global_extern = 0;
*pointer_to_global_extern_untagged = 0;
*pointer_to_global_extern_untagged_definition_but_tagged_import = 0;
if (check_pointers_are_tagged) {
for (const auto& [_, pointer] : get_expected_tagged_vars()) {
check_tagged(pointer);
}
}
for (const auto& [_, pointer] : get_expected_untagged_vars()) {
check_untagged(pointer);
}
check_matching_tags(pointer_to_global, pointer_inside_global);
check_matching_tags(pointer_to_global, pointer_to_global_end);
check_matching_tags(pointer_to_global, pointer_past_global_end);
check_eq(pointer_inside_global, pointer_to_global + 17);
check_eq(pointer_to_global_end, pointer_to_global + 30);
check_eq(pointer_past_global_end, pointer_to_global + 48);
check_matching_tags(pointer_to_hidden_global, pointer_to_hidden_global_end);
check_matching_tags(pointer_to_hidden_global, pointer_past_hidden_global_end);
check_eq(pointer_to_hidden_global_end, pointer_to_hidden_global + 12);
check_eq(pointer_past_hidden_global_end, pointer_to_hidden_global + 16);
}
void crash() {
*pointer_past_global_end = 0;
}
int main(int argc, char** argv) {
bool check_pointers_are_tagged = false;
// For an MTE-capable device, provide argv[1] == '1' to enable the assertions
// that pointers should be tagged.
if (argc >= 2 && argv[1][0] == '1') {
check_pointers_are_tagged = true;
}
char* heap_ptr = static_cast<char*>(malloc(1));
print_variable_address("heap address", heap_ptr);
*heap_ptr = 0;
if (check_pointers_are_tagged) check_tagged(heap_ptr);
free(heap_ptr);
exe_print_variables();
dso_print_variables();
exe_check_assertions(check_pointers_are_tagged);
dso_check_assertions(check_pointers_are_tagged);
printf("Assertions were passed. Now doing a global-buffer-overflow.\n");
fflush(stdout);
crash();
printf("global-buffer-overflow went uncaught.\n");
return 0;
}

View file

@ -1,165 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include "memtag_globals.h"
// Adapted from the LLD test suite: lld/test/ELF/Inputs/aarch64-memtag-globals.s
int global_extern;
static int global_extern_hidden;
__attribute__((no_sanitize("memtag"))) int global_extern_untagged;
__attribute__((no_sanitize("memtag"))) int global_extern_untagged_definition_but_tagged_import;
void assertion_failure() {
exit(1);
}
void check_tagged(const void* a) {
uintptr_t a_uptr = reinterpret_cast<uintptr_t>(a);
#if defined(__aarch64__)
if ((a_uptr >> 56) == 0) {
fprintf(stderr, "**********************************\n");
fprintf(stderr, "Failed assertion:\n");
fprintf(stderr, " tag(0x%zx) != 0\n", a_uptr);
fprintf(stderr, "**********************************\n");
assertion_failure();
}
#endif // defined(__aarch64__)
}
void check_untagged(const void* a) {
uintptr_t a_uptr = reinterpret_cast<uintptr_t>(a);
#if defined(__aarch64__)
if ((a_uptr >> 56) != 0) {
fprintf(stderr, "**********************************\n");
fprintf(stderr, "Failed assertion:\n");
fprintf(stderr, " tag(0x%zx) == 0\n", a_uptr);
fprintf(stderr, "**********************************\n");
assertion_failure();
}
#endif // defined(__aarch64__)
}
void check_matching_tags(const void* a, const void* b) {
uintptr_t a_uptr = reinterpret_cast<uintptr_t>(a);
uintptr_t b_uptr = reinterpret_cast<uintptr_t>(b);
#if defined(__aarch64__)
if (a_uptr >> 56 != b_uptr >> 56) {
fprintf(stderr, "**********************************\n");
fprintf(stderr, "Failed assertion:\n");
fprintf(stderr, " tag(0x%zx) != tag(0x%zx)\n", a_uptr, b_uptr);
fprintf(stderr, "**********************************\n");
assertion_failure();
}
#endif // defined(__aarch64__)
}
void check_eq(const void* a, const void* b) {
if (a != b) {
fprintf(stderr, "**********************************\n");
fprintf(stderr, "Failed assertion:\n");
fprintf(stderr, " %p != %p\n", a, b);
fprintf(stderr, "**********************************\n");
assertion_failure();
}
}
#define LONGEST_VARIABLE_NAME "51"
void print_variable_address(const char* name, const void* ptr) {
printf("%" LONGEST_VARIABLE_NAME "s: %16p\n", name, ptr);
}
static const std::vector<std::pair<const char*, const void*>>& get_expected_tagged_vars() {
static std::vector<std::pair<const char*, const void*>> expected_tagged_vars = {
{"global_extern", &global_extern},
{"global_extern_hidden", &global_extern_hidden},
};
return expected_tagged_vars;
}
static const std::vector<std::pair<const char*, const void*>>& get_expected_untagged_vars() {
static std::vector<std::pair<const char*, const void*>> expected_untagged_vars = {
{"global_extern_untagged", &global_extern_untagged},
{"global_extern_untagged_definition_but_tagged_import",
&global_extern_untagged_definition_but_tagged_import},
};
return expected_untagged_vars;
}
void dso_print_variables() {
print_variables(" Variables declared in the DSO:\n", get_expected_tagged_vars(),
get_expected_untagged_vars());
}
void print_variables(const char* header,
const std::vector<std::pair<const char*, const void*>>& tagged_variables,
const std::vector<std::pair<const char*, const void*>>& untagged_variables) {
printf("==========================================================\n");
printf("%s", header);
printf("==========================================================\n");
printf(" Variables expected to be tagged:\n");
printf("----------------------------------------------------------\n");
for (const auto& [name, pointer] : tagged_variables) {
print_variable_address(name, pointer);
}
printf("\n----------------------------------------------------------\n");
printf(" Variables expected to be untagged:\n");
printf("----------------------------------------------------------\n");
for (const auto& [name, pointer] : untagged_variables) {
print_variable_address(name, pointer);
}
printf("\n");
}
void dso_check_assertions(bool check_pointers_are_tagged) {
// Check that non-const variables are writeable.
global_extern = 0;
global_extern_hidden = 0;
global_extern_untagged = 0;
global_extern_untagged_definition_but_tagged_import = 0;
if (check_pointers_are_tagged) {
for (const auto& [_, pointer] : get_expected_tagged_vars()) {
check_tagged(pointer);
}
}
for (const auto& [_, pointer] : get_expected_untagged_vars()) {
check_untagged(pointer);
}
}