diff -pruN 1.3.1+dfsg-1/cmdline.h 1.4.0+dfsg-1/cmdline.h
--- 1.3.1+dfsg-1/cmdline.h	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/cmdline.h	2022-08-04 12:47:21.000000000 +0000
@@ -61,7 +61,7 @@ std::vector<std::string_view> expand_res
   return vec;
 }
 
-static std::string_view string_trim(std::string_view str) {
+static inline std::string_view string_trim(std::string_view str) {
   size_t pos = str.find_first_not_of(" \t");
   if (pos == str.npos)
     return "";
diff -pruN 1.3.1+dfsg-1/debian/changelog 1.4.0+dfsg-1/debian/changelog
--- 1.3.1+dfsg-1/debian/changelog	2022-07-02 09:25:04.000000000 +0000
+++ 1.4.0+dfsg-1/debian/changelog	2022-08-05 07:43:13.000000000 +0000
@@ -1,3 +1,23 @@
+mold (1.4.0+dfsg-1) unstable; urgency=medium
+
+  [ Roger Shimizu ]
+  * d/control:
+    - Sort B-D list.
+    - Remove libstdc++-10-dev and unused clang from B-D list.
+    - Add libmimalloc-dev and libtbb-dev to B-D list.
+  * debian/rules:
+    - Prefer to use system library mimalloc and tbb.
+  * debian/patches:
+    - Add patch to avoid overwriting C*FLAGS set by debian/rules.
+
+  [ Sylvestre Ledru ]
+  * New upstream release
+  * Add gdb as a build dep for tests
+  * For now, use TBB from the tree
+    See https://github.com/rui314/mold/issues/600
+
+ -- Sylvestre Ledru <sylvestre@debian.org>  Fri, 05 Aug 2022 09:43:13 +0200
+
 mold (1.3.1+dfsg-1) unstable; urgency=medium
 
   * New upstream release
diff -pruN 1.3.1+dfsg-1/debian/control 1.4.0+dfsg-1/debian/control
--- 1.3.1+dfsg-1/debian/control	2022-06-18 07:34:33.000000000 +0000
+++ 1.4.0+dfsg-1/debian/control	2022-08-05 07:43:13.000000000 +0000
@@ -2,9 +2,17 @@ Source: mold
 Section: devel
 Priority: optional
 Maintainer: Sylvestre Ledru <sylvestre@debian.org>
-Build-Depends: debhelper-compat (= 13),
- clang, cmake, libstdc++-10-dev, libssl-dev, libxxhash-dev,
- zlib1g-dev, pkg-config, dwarfdump
+Build-Depends:
+ cmake,
+ debhelper-compat (= 13),
+ dwarfdump,
+ libmimalloc-dev,
+ libssl-dev,
+ libtbb-dev,
+ libxxhash-dev,
+ pkg-config,
+ zlib1g-dev,
+ gdb
 Standards-Version: 4.6.0
 Homepage: https://github.com/rui314/mold
 Vcs-Browser: https://salsa.debian.org/pkg-llvm-team/mold
@@ -19,4 +27,3 @@ Description: Drop-in linker
  It is several times faster than the LLVM lld linker.
  mold is designed to increase developer productivity by reducing
  build time, especially in rapid debug-edit-rebuild cycles.
-
diff -pruN 1.3.1+dfsg-1/debian/copyright 1.4.0+dfsg-1/debian/copyright
--- 1.3.1+dfsg-1/debian/copyright	2022-06-18 07:34:33.000000000 +0000
+++ 1.4.0+dfsg-1/debian/copyright	2022-08-05 07:43:13.000000000 +0000
@@ -7,7 +7,6 @@ Files-Excluded: third-party/mimalloc/doc
                 third-party/mimalloc/bin/mimalloc-redirect32.dll
                 third-party/mimalloc/bin/minject.exe
                 third-party/mimalloc/bin/minject32.exe
-                docs/mold.jpg
 
 Files: *
 Copyright: 2020-2021 Rui Ueyama <ruiu@cs.stanford.edu>
diff -pruN 1.3.1+dfsg-1/debian/patches/avoid-overwriting-CFLAGS.diff 1.4.0+dfsg-1/debian/patches/avoid-overwriting-CFLAGS.diff
--- 1.3.1+dfsg-1/debian/patches/avoid-overwriting-CFLAGS.diff	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/debian/patches/avoid-overwriting-CFLAGS.diff	2022-08-05 07:43:13.000000000 +0000
@@ -0,0 +1,19 @@
+Index: mold/Makefile
+===================================================================
+--- mold.orig/Makefile
++++ mold/Makefile
+@@ -39,10 +39,10 @@ ifneq ($(findstring -android,$(shell $(C
+   IS_ANDROID = 1
+ endif
+ 
+-# If you want to compile mold for debugging, invoke make as
+-# `make CXXFLAGS=-g`.
+-CFLAGS = -O2
+-CXXFLAGS = -O2
++# Avoid overwriting C*FLAGS set by debian/rules, such as hardening.
++# And we already have -O2 by default, so no need to append here.
++#CFLAGS = -O2
++#CXXFLAGS = -O2
+ 
+ MOLD_CXXFLAGS := -std=c++20 -fno-exceptions -fno-unwind-tables \
+                  -fno-asynchronous-unwind-tables -Ithird-party \
diff -pruN 1.3.1+dfsg-1/debian/patches/fix-prefix.diff 1.4.0+dfsg-1/debian/patches/fix-prefix.diff
--- 1.3.1+dfsg-1/debian/patches/fix-prefix.diff	2022-07-02 09:25:04.000000000 +0000
+++ 1.4.0+dfsg-1/debian/patches/fix-prefix.diff	2022-08-05 07:43:13.000000000 +0000
@@ -3,7 +3,7 @@ Index: mold/Makefile
 --- mold.orig/Makefile
 +++ mold/Makefile
 @@ -1,6 +1,6 @@
- VERSION = 1.3.1
+ VERSION = 1.4.0
  
 -PREFIX = /usr/local
 +PREFIX = /usr
diff -pruN 1.3.1+dfsg-1/debian/patches/fix-tbb.diff 1.4.0+dfsg-1/debian/patches/fix-tbb.diff
--- 1.3.1+dfsg-1/debian/patches/fix-tbb.diff	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/debian/patches/fix-tbb.diff	2022-08-05 07:43:13.000000000 +0000
@@ -0,0 +1,14 @@
+Index: mold/Makefile
+===================================================================
+--- mold.orig/Makefile
++++ mold/Makefile
+@@ -155,7 +155,8 @@ $(MIMALLOC_LIB):
+ 
+ $(TBB_LIB):
+ 	mkdir -p out/tbb
+-	(cd out/tbb; cmake -G'Unix Makefiles' -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=OFF -DTBB_TEST=OFF -DCMAKE_CXX_FLAGS="$(CXXFLAGS) -D__TBB_DYNAMIC_LOAD_ENABLED=0" -DTBB_STRICT=OFF ../../third-party/tbb)
++	CXXFLAGS_TBB:=$(filter-out -Werror=format-security,$(CXXFLAGS)); \
++	(cd out/tbb; cmake -G'Unix Makefiles' -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=OFF -DTBB_TEST=OFF -DCMAKE_CXX_FLAGS="$$CXXFLAGS_TBB -D__TBB_DYNAMIC_LOAD_ENABLED=0" -DTBB_STRICT=OFF ../../third-party/tbb)
+ 	$(MAKE) -C out/tbb tbb
+ 	(cd out/tbb; ln -sf *_relwithdebinfo libs)
+ 
diff -pruN 1.3.1+dfsg-1/debian/patches/series 1.4.0+dfsg-1/debian/patches/series
--- 1.3.1+dfsg-1/debian/patches/series	2022-06-18 07:40:27.000000000 +0000
+++ 1.4.0+dfsg-1/debian/patches/series	2022-08-05 07:43:13.000000000 +0000
@@ -1,2 +1,4 @@
 pie.diff
 fix-prefix.diff
+avoid-overwriting-CFLAGS.diff
+fix-tbb.diff
diff -pruN 1.3.1+dfsg-1/debian/rules 1.4.0+dfsg-1/debian/rules
--- 1.3.1+dfsg-1/debian/rules	2022-02-21 07:42:10.000000000 +0000
+++ 1.4.0+dfsg-1/debian/rules	2022-08-05 07:43:13.000000000 +0000
@@ -12,5 +12,11 @@ export DEB_CFLAGS_MAINT_APPEND  = -Wall
 # package maintainers to append LDFLAGS
 export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
 
+export SYSTEM_MIMALLOC = 1
+#export SYSTEM_TBB = 1
+
 %:
 	dh $@
+
+override_dh_auto_configure:
+	dh_auto_configure
diff -pruN 1.3.1+dfsg-1/demangle.cc 1.4.0+dfsg-1/demangle.cc
--- 1.3.1+dfsg-1/demangle.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/demangle.cc	2022-08-04 12:47:21.000000000 +0000
@@ -2,14 +2,33 @@
 
 #include <cstdlib>
 #include <cxxabi.h>
+#include <rust-demangle/rust-demangle.h>
 
 namespace mold {
 
 std::string_view demangle(std::string_view name) {
-  if (name.starts_with("_Z")) {
-    static thread_local char *buf;
-    static thread_local size_t buflen;
+  static thread_local char *p;
+  if (p)
+    free(p);
+
+  // Try to demangle as a Rust symbol. Since legacy-style Rust symbols
+  // are also valid as a C++ mangled name, we need to call this before
+  // cpp_demangle.
+  p = rust_demangle(std::string(name).c_str(), 0);
+  if (p)
+    return p;
+
+  // Try to demangle as a C++ symbol.
+  if (std::optional<std::string_view> s = cpp_demangle(name))
+    return *s;
+  return name;
+}
+
+std::optional<std::string_view> cpp_demangle(std::string_view name) {
+  static thread_local char *buf;
+  static thread_local size_t buflen;
 
+  if (name.starts_with("_Z")) {
     int status;
     char *p = abi::__cxa_demangle(std::string(name).c_str(), buf, &buflen, &status);
     if (status == 0) {
@@ -17,8 +36,7 @@ std::string_view demangle(std::string_vi
       return p;
     }
   }
-
-  return name;
+  return {};
 }
 
 } // namespace mold
diff -pruN 1.3.1+dfsg-1/docs/mold.1 1.4.0+dfsg-1/docs/mold.1
--- 1.3.1+dfsg-1/docs/mold.1	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/docs/mold.1	2022-08-04 12:47:21.000000000 +0000
@@ -529,6 +529,10 @@ options.
 .It Fl -dynamic-list Ns = Ns Ar file
 Read a list of dynamic symbols from
 .Ar file .
+Same as
+.Fl -export-dynamic-symbol-list ,
+except that it implies
+.Fl -Bsymbolic .
 .Pp
 .It Fl -eh-frame-hdr
 .It Fl -no-eh-frame-hdr
@@ -569,6 +573,20 @@ Mark all symbols in the given
 .Ar libraries
 hidden.
 .Pp
+.It Fl -export-dynamic-symbol Ns = Ns Ar sym
+Put symbols matching
+.Ar sym
+in the dynamic symbol table.
+.Ar sym
+may be a glob, with the same syntax as the globs used in
+.Fl -export-dynamic-symbol-list
+or
+.Fl -version-script .
+.Pp
+.It Fl -export-dynamic-symbol-list Ns = Ns Ar file
+Read a list of dynamic symbols from
+.Ar file .
+.Pp
 .It Fl -fatal-warnings
 .It Fl -no-fatal-warnings
 Treat warnings as errors.
@@ -639,8 +657,8 @@ with something else without taking an ad
 needs to be used with a compiler that supports
 .Sy .llvm_addrsig
 section which contains the information as to what symbols are address-taken. \
-LLVM/Clang supports that section by default. Since GCC does not suppor it yet, \
-you cannot use
+LLVM/Clang supports that section by default. Since GCC does not support it \
+yet, you cannot use
 .Fl -icf=safe
 with GCC (it doesn't do any harm but can't optimize at all.)
 .Pp
diff -pruN 1.3.1+dfsg-1/elf/arch-arm32.cc 1.4.0+dfsg-1/elf/arch-arm32.cc
--- 1.3.1+dfsg-1/elf/arch-arm32.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/arch-arm32.cc	2022-08-04 12:47:21.000000000 +0000
@@ -136,7 +136,7 @@ void InputSection<E>::apply_reloc_alloc(
 
   for (i64 i = 0; i < rels.size(); i++) {
     const ElfRel<E> &rel = rels[i];
-    if (rel.r_type == R_ARM_NONE)
+    if (rel.r_type == R_ARM_NONE || rel.r_type == R_ARM_V4BX)
       continue;
 
     Symbol<E> &sym = *file.symbols[rel.r_sym];
@@ -272,11 +272,11 @@ void InputSection<E>::apply_reloc_alloc(
       *(ul32 *)loc = sym.get_gottp_addr(ctx) + A - P;
       continue;
     case R_ARM_TLS_LE32:
-      *(ul32 *)loc = S + A - ctx.tls_begin + 8;
+      *(ul32 *)loc = S + A - ctx.tls_begin + E::tls_offset;
       continue;
     case R_ARM_TLS_GOTDESC:
       if (sym.get_tlsdesc_idx(ctx) == -1)
-        *(ul32 *)loc = S - ctx.tls_begin + 8;
+        *(ul32 *)loc = S - ctx.tls_begin + E::tls_offset;
       else
         *(ul32 *)loc = sym.get_tlsdesc_addr(ctx) + A - P - 6;
       continue;
@@ -455,6 +455,7 @@ void InputSection<E>::scan_relocations(C
     case R_ARM_TLS_LDO32:
     case R_ARM_TLS_LE32:
     case R_ARM_THM_TLS_CALL:
+    case R_ARM_V4BX:
       break;
     default:
       Error(ctx) << *this << ": unknown relocation: " << rel;
@@ -515,6 +516,14 @@ void TlsTrampolineSection::copy_buf(Cont
   memcpy(ctx.buf + this->shdr.sh_offset, insn, sizeof(insn));
 }
 
+template <typename E>
+static OutputSection<E> *find_exidx_section(Context<E> &ctx) {
+  for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
+    if (osec->shdr.sh_type == SHT_ARM_EXIDX)
+      return osec.get();
+  return nullptr;
+}
+
 // ARM executables use an .ARM.exidx section to look up an exception
 // handling record for the current instruction pointer. The table needs
 // to be sorted by their addresses.
@@ -527,14 +536,7 @@ void TlsTrampolineSection::copy_buf(Cont
 void sort_arm_exidx(Context<E> &ctx) {
   Timer t(ctx, "sort_arm_exidx");
 
-  auto find_exidx = [&]() -> OutputSection<E> * {
-    for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
-      if (osec->shdr.sh_type == SHT_ARM_EXIDX)
-        return osec.get();
-    return nullptr;
-  };
-
-  OutputSection<E> *osec = find_exidx();
+  OutputSection<E> *osec = find_exidx_section(ctx);
   if (!osec)
     return;
 
@@ -548,8 +550,10 @@ void sort_arm_exidx(Context<E> &ctx) {
   // 3. a 31-bit relative address which points to a larger record in
   //    the .ARM.extab section.
   //
-  // CANTUNWIND is value 1. The most significant is set in (2) but not
-  // in (3). So they can be distinguished just by looking at a value.
+  // CANTUNWIND is value 1. The most significant bit is set in (2) but
+  // not in (3). So we can distinguished them just by looking at a value.
+  const u32 EXIDX_CANTUNWIND = 1;
+
   struct Entry {
     ul32 addr;
     ul32 val;
@@ -558,38 +562,36 @@ void sort_arm_exidx(Context<E> &ctx) {
   if (osec->shdr.sh_size % sizeof(Entry))
     Fatal(ctx) << "invalid .ARM.exidx section size";
 
-  Entry *begin = (Entry *)(ctx.buf + osec->shdr.sh_offset);
-  Entry *end = (Entry *)(ctx.buf + osec->shdr.sh_offset + osec->shdr.sh_size);
+  Entry *ent = (Entry *)(ctx.buf + osec->shdr.sh_offset);
+  i64 num_entries = osec->shdr.sh_size / sizeof(Entry);
 
-  struct Entry2 {
-    u32 addr;
-    u32 val;
-    u32 idx;
+  // Entry's addresses are relative to the beginning of their entries.
+  // We first translate them so that they are relative to the
+  // beginning of the section. We then sort the records and then
+  // translate them back to be relative to each record.
+
+  auto is_relative = [](u32 val) {
+    return val != EXIDX_CANTUNWIND && !(val & 0x8000'0000);
   };
 
-  // Read section contents
-  std::vector<Entry2> vec;
-  vec.reserve(end - begin);
-  for (Entry *it = begin; it < end; it++)
-    vec.push_back({it->addr, it->val, (u32)(it - begin)});
-
-  // Sort the records
-  tbb::parallel_sort(vec.begin(), vec.end(), [](const Entry2 &a, const Entry2 &b) {
-    return sign_extend(a.addr, 30) + a.idx * sizeof(Entry) <
-           sign_extend(b.addr, 30) + b.idx * sizeof(Entry);
+  tbb::parallel_for((i64)0, num_entries, [&](i64 i) {
+    i64 offset = sizeof(Entry) * i;
+    ent[i].addr = sign_extend(ent[i].addr, 30) - offset;
+    if (is_relative(ent[i].val))
+      ent[i].val = 0x7fff'ffff & (sign_extend(ent[i].val, 30) - offset);
+  });
+
+  tbb::parallel_sort(ent, ent + num_entries, [](const Entry &a, const Entry &b) {
+    return a.addr < b.addr;
   });
 
   // Write back the sorted records while adjusting relative addresses
-  for (i64 i = 0; i < vec.size(); i++) {
-    u32 offset = (vec[i].idx - i) * sizeof(Entry);
-    begin[i].addr = 0x7fff'ffff & (sign_extend(vec[i].addr, 30) + offset);
-
-    const u32 EXIDX_CANTUNWIND = 1;
-    if (vec[i].val == EXIDX_CANTUNWIND || (vec[i].val & 0x8000'0000))
-      begin[i].val = vec[i].val;
-    else
-      begin[i].val = 0x7fff'ffff & (sign_extend(vec[i].val, 30) + offset);
-  }
+  tbb::parallel_for((i64)0, num_entries, [&](i64 i) {
+    i64 offset = sizeof(Entry) * i;
+    ent[i].addr = 0x7fff'ffff & (ent[i].addr + offset);
+    if (is_relative(ent[i].val))
+      ent[i].val = 0x7fff'ffff & (ent[i].val + offset);
+  });
 }
 
 } // namespace mold::elf
diff -pruN 1.3.1+dfsg-1/elf/arch-arm64.cc 1.4.0+dfsg-1/elf/arch-arm64.cc
--- 1.3.1+dfsg-1/elf/arch-arm64.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/arch-arm64.cc	2022-08-04 12:47:21.000000000 +0000
@@ -48,7 +48,7 @@ static void write_plt_header(Context<E>
 }
 
 static void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
-  u8 *ent = buf + ctx.plt_hdr_size + sym.get_plt_idx(ctx) * ctx.plt_size;
+  u8 *ent = buf + E::plt_hdr_size + sym.get_plt_idx(ctx) * E::plt_size;
 
   static const u32 data[] = {
     0x90000010, // adrp x16, .got.plt[n]
@@ -279,14 +279,14 @@ void InputSection<E>::apply_reloc_alloc(
       *(ul32 *)loc |= bits(sym.get_gottp_addr(ctx) + A, 11, 3) << 10;
       continue;
     case R_AARCH64_TLSLE_ADD_TPREL_HI12: {
-      i64 val = S + A - ctx.tls_begin + 16;
+      i64 val = S + A - ctx.tls_begin + E::tls_offset;
       overflow_check(val, 0, (i64)1 << 24);
       *(ul32 *)loc |= bits(val, 23, 12) << 10;
       continue;
     }
     case R_AARCH64_TLSLE_ADD_TPREL_LO12:
     case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
-      *(ul32 *)loc |= bits(S + A - ctx.tls_begin + 16, 11, 0) << 10;
+      *(ul32 *)loc |= bits(S + A - ctx.tls_begin + E::tls_offset, 11, 0) << 10;
       continue;
     case R_AARCH64_TLSGD_ADR_PAGE21: {
       i64 val = page(sym.get_tlsgd_addr(ctx) + A) - page(P);
@@ -300,7 +300,7 @@ void InputSection<E>::apply_reloc_alloc(
     case R_AARCH64_TLSDESC_ADR_PAGE21: {
       if (ctx.relax_tlsdesc && !sym.is_imported) {
         // adrp x0, 0 -> movz x0, #tls_ofset_hi, lsl #16
-        i64 val = (S + A - ctx.tls_begin + 16);
+        i64 val = (S + A - ctx.tls_begin + E::tls_offset);
         overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
         *(ul32 *)loc = 0xd2a00000 | (bits(val, 32, 16) << 5);
       } else {
@@ -313,7 +313,7 @@ void InputSection<E>::apply_reloc_alloc(
     case R_AARCH64_TLSDESC_LD64_LO12:
       if (ctx.relax_tlsdesc && !sym.is_imported) {
         // ldr x2, [x0] -> movk x0, #tls_ofset_lo
-        u32 offset_lo = (S + A - ctx.tls_begin + 16) & 0xffff;
+        u32 offset_lo = (S + A - ctx.tls_begin + E::tls_offset) & 0xffff;
         *(ul32 *)loc = 0xf2800000 | (offset_lo << 5);
       } else {
         *(ul32 *)loc |= bits(sym.get_tlsdesc_addr(ctx) + A, 11, 3) << 10;
@@ -497,7 +497,7 @@ static void reset_thunk(RangeExtensionTh
   for (Symbol<E> *sym : thunk.symbols) {
     sym->extra.thunk_idx = -1;
     sym->extra.thunk_sym_idx = -1;
-    sym->flags &= (u8)~NEEDS_RANGE_EXTN_THUNK;
+    sym->flags = 0;
   }
 }
 
diff -pruN 1.3.1+dfsg-1/elf/arch-i386.cc 1.4.0+dfsg-1/elf/arch-i386.cc
--- 1.3.1+dfsg-1/elf/arch-i386.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/arch-i386.cc	2022-08-04 12:47:21.000000000 +0000
@@ -4,6 +4,10 @@ namespace mold::elf {
 
 using E = I386;
 
+// Emitting position-independent code (PIC) for i386 is a bit tricky
+// because i386 doesn't support PC-relative memory access instructions.
+// By default, i386 executables are not PIC. If PIC, %ebx is used to
+// store the location of .got and access all data with offsets from .got.
 static void write_plt_header(Context<E> &ctx, u8 *buf) {
   if (ctx.arg.pic) {
     static const u8 plt0[] = {
@@ -28,7 +32,7 @@ static void write_plt_header(Context<E>
 
 static void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym,
                             i64 idx) {
-  u8 *ent = buf + ctx.plt_hdr_size + sym.get_plt_idx(ctx) * ctx.plt_size;
+  u8 *ent = buf + E::plt_hdr_size + sym.get_plt_idx(ctx) * E::plt_size;
 
   if (ctx.arg.pic) {
     static const u8 data[] = {
@@ -137,30 +141,6 @@ void InputSection<E>::apply_reloc_alloc(
                    << lo << ", " << hi << ")";
     };
 
-    auto write8 = [&](u64 val) {
-      overflow_check(val, 0, 1 << 8);
-      *loc = val;
-    };
-
-    auto write8s = [&](u64 val) {
-      overflow_check(val, -(1 << 7), 1 << 7);
-      *loc = val;
-    };
-
-    auto write16 = [&](u64 val) {
-      overflow_check(val, 0, 1 << 16);
-      *(ul16 *)loc = val;
-    };
-
-    auto write16s = [&](u64 val) {
-      overflow_check(val, -(1 << 15), 1 << 15);
-      *(ul16 *)loc = val;
-    };
-
-    auto write32 = [&](u64 val) {
-      *(ul32 *)loc = val;
-    };
-
 #define S   (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
 #define A   (frag_ref ? frag_ref->addend : this->get_addend(rel))
 #define P   (output_section->shdr.sh_addr + offset + rel.r_offset)
@@ -168,59 +148,71 @@ void InputSection<E>::apply_reloc_alloc(
 #define GOT ctx.got->shdr.sh_addr
 
     switch (rel.r_type) {
-    case R_386_8:
-      write8(S + A);
+    case R_386_8: {
+      i64 val = S + A;
+      overflow_check(val, 0, 1 << 8);
+      *loc = val;
       continue;
-    case R_386_16:
-      write16(S + A);
+    }
+    case R_386_16: {
+      i64 val = S + A;
+      overflow_check(val, 0, 1 << 16);
+      *(ul16 *)loc = val;
       continue;
+    }
     case R_386_32:
       if (sym.is_absolute() || !ctx.arg.pic) {
-        write32(S + A);
+        *(ul32 *)loc = S + A;
       } else if (sym.is_imported) {
         *dynrel++ = {P, R_386_32, (u32)sym.get_dynsym_idx(ctx)};
-        write32(A);
+        *(ul32 *)loc = A;
       } else {
         if (!is_relr_reloc(ctx, rel))
           *dynrel++ = {P, R_386_RELATIVE, 0};
-        write32(S + A);
+        *(ul32 *)loc = S + A;
       }
       continue;
-    case R_386_PC8:
-      write8s(S + A);
+    case R_386_PC8: {
+      i64 val = S + A;
+      overflow_check(val, -(1 << 7), 1 << 7);
+      *loc = val;
       continue;
-    case R_386_PC16:
-      write16s(S + A);
+    };
+    case R_386_PC16: {
+      i64 val = S + A;
+      overflow_check(val, -(1 << 15), 1 << 15);
+      *(ul16 *)loc = val;
       continue;
+    }
     case R_386_PC32:
       if (sym.is_absolute() || !sym.is_imported || !ctx.arg.shared) {
-        write32(S + A - P);
+        *(ul32 *)loc = S + A - P;
       } else {
         *dynrel++ = {P, R_386_32, (u32)sym.get_dynsym_idx(ctx)};
-        write32(A);
+        *(ul32 *)loc = A;
       }
       continue;
     case R_386_PLT32:
-      write32(S + A - P);
+      *(ul32 *)loc = S + A - P;
       continue;
     case R_386_GOT32:
     case R_386_GOT32X:
-      write32(G + A);
+      *(ul32 *)loc = G + A;
       continue;
     case R_386_GOTOFF:
-      write32(S + A - GOT);
+      *(ul32 *)loc = S + A - GOT;
       continue;
     case R_386_GOTPC:
-      write32(GOT + A - P);
+      *(ul32 *)loc = GOT + A - P;
       continue;
     case R_386_TLS_GOTIE:
-      write32(sym.get_gottp_addr(ctx) + A - GOT);
+      *(ul32 *)loc = sym.get_gottp_addr(ctx) + A - GOT;
       continue;
     case R_386_TLS_LE:
-      write32(S + A - ctx.tls_end);
+      *(ul32 *)loc = S + A - ctx.tls_end;
       continue;
     case R_386_TLS_IE:
-      write32(sym.get_gottp_addr(ctx) + A);
+      *(ul32 *)loc = sym.get_gottp_addr(ctx) + A;
       continue;
     case R_386_TLS_GD:
       if (sym.get_tlsgd_idx(ctx) == -1) {
@@ -250,7 +242,7 @@ void InputSection<E>::apply_reloc_alloc(
 
         i++;
       } else {
-        write32(sym.get_tlsgd_addr(ctx) + A - GOT);
+        *(ul32 *)loc = sym.get_tlsgd_addr(ctx) + A - GOT;
       }
       continue;
     case R_386_TLS_LDM:
@@ -281,17 +273,17 @@ void InputSection<E>::apply_reloc_alloc(
 
         i++;
       } else {
-        write32(ctx.got->get_tlsld_addr(ctx) + A - GOT);
+        *(ul32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - GOT;
       }
       continue;
     case R_386_TLS_LDO_32:
       if (ctx.got->tlsld_idx == -1)
-        write32(S + A - ctx.tls_end);
+        *(ul32 *)loc = S + A - ctx.tls_end;
       else
-        write32(S + A - ctx.tls_begin);
+        *(ul32 *)loc = S + A - ctx.tls_begin;
       continue;
     case R_386_SIZE32:
-      write32(sym.esym().st_size + A);
+      *(ul32 *)loc = sym.esym().st_size + A;
       continue;
     case R_386_TLS_GOTDESC:
       if (sym.get_tlsdesc_idx(ctx) == -1) {
@@ -299,9 +291,9 @@ void InputSection<E>::apply_reloc_alloc(
           0x8d, 0x05, 0, 0, 0, 0, // lea 0, %eax
         };
         memcpy(loc - 2, insn, sizeof(insn));
-        write32(S + A - ctx.tls_end);
+        *(ul32 *)loc = S + A - ctx.tls_end;
       } else {
-        write32(sym.get_tlsdesc_addr(ctx) + A - GOT);
+        *(ul32 *)loc = sym.get_tlsdesc_addr(ctx) + A - GOT;
       }
       continue;
     case R_386_TLS_DESC_CALL:
@@ -351,38 +343,24 @@ void InputSection<E>::apply_reloc_nonall
                    << lo << ", " << hi << ")";
     };
 
-    auto write8 = [&](u64 val) {
-      overflow_check(val, 0, 1 << 8);
-      *loc = val;
-    };
-
-    auto write8s = [&](u64 val) {
-      overflow_check(val, -(1 << 7), 1 << 7);
-      *loc = val;
-    };
-
-    auto write16 = [&](u64 val) {
-      overflow_check(val, 0, 1 << 16);
-      *(ul16 *)loc = val;
-    };
-
-    auto write16s = [&](u64 val) {
-      overflow_check(val, -(1 << 15), 1 << 15);
-      *(ul16 *)loc = val;
-    };
-
 #define S   (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
 #define A   (frag ? addend : this->get_addend(rel))
 #define G   (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
 #define GOT ctx.got->shdr.sh_addr
 
     switch (rel.r_type) {
-    case R_386_8:
-      write8(S + A);
+    case R_386_8: {
+      i64 val = S + A;
+      overflow_check(val, 0, 1 << 8);
+      *loc = val;
       continue;
-    case R_386_16:
-      write16(S + A);
+    }
+    case R_386_16: {
+      i64 val = S + A;
+      overflow_check(val, 0, 1 << 16);
+      *(ul16 *)loc = val;
       continue;
+    }
     case R_386_32:
       if (!frag) {
         if (std::optional<u64> val = get_tombstone(sym)) {
@@ -392,12 +370,18 @@ void InputSection<E>::apply_reloc_nonall
       }
       *(ul32 *)loc = S + A;
       continue;
-    case R_386_PC8:
-      write8s(S + A);
+    case R_386_PC8: {
+      i64 val = S + A;
+      overflow_check(val, -(1 << 7), 1 << 7);
+      *loc = val;
       continue;
-    case R_386_PC16:
-      write16s(S + A);
+    }
+    case R_386_PC16: {
+      i64 val = S + A;
+      overflow_check(val, -(1 << 15), 1 << 15);
+      *(ul16 *)loc = val;
       continue;
+    };
     case R_386_PC32:
       *(ul32 *)loc = S + A;
       continue;
diff -pruN 1.3.1+dfsg-1/elf/arch-riscv64.cc 1.4.0+dfsg-1/elf/arch-riscv64.cc
--- 1.3.1+dfsg-1/elf/arch-riscv64.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/arch-riscv64.cc	1970-01-01 00:00:00.000000000 +0000
@@ -1,797 +0,0 @@
-#include "mold.h"
-
-#include <tbb/parallel_for.h>
-#include <tbb/parallel_for_each.h>
-
-namespace mold::elf {
-
-using E = RISCV64;
-
-static u32 itype(u32 val) {
-  return val << 20;
-}
-
-static u32 stype(u32 val) {
-  return bits(val, 11, 5) << 25 | bits(val, 4, 0) << 7;
-}
-
-static u32 btype(u32 val) {
-  return bit(val, 12) << 31 | bits(val, 10, 5) << 25 |
-         bits(val, 4, 1) << 8 | bit(val, 11) << 7;
-}
-
-static u32 utype(u32 val) {
-  // U-type instructions are used in combination with I-type
-  // instructions. U-type insn sets an immediate to the upper 20-bits
-  // of a register. I-type insn sign-extends a 12-bits immediate and
-  // add it to a register value to construct a complete value. 0x800
-  // is added here to compensate for the sign-extension.
-  return bits(val + 0x800, 31, 12) << 12;
-}
-
-static u32 jtype(u32 val) {
-  return bit(val, 20) << 31 | bits(val, 10, 1)  << 21 |
-         bit(val, 11) << 20 | bits(val, 19, 12) << 12;
-}
-
-static u32 cbtype(u32 val) {
-  return bit(val, 8) << 12 | bit(val, 4) << 11 | bit(val, 3) << 10 |
-         bit(val, 7) << 6  | bit(val, 6) << 5  | bit(val, 2) << 4  |
-         bit(val, 1) << 3  | bit(val, 5) << 2;
-}
-
-static u32 cjtype(u32 val) {
-  return bit(val, 11) << 12 | bit(val, 4)  << 11 | bit(val, 9) << 10 |
-         bit(val, 8)  << 9  | bit(val, 10) << 8  | bit(val, 6) << 7  |
-         bit(val, 7)  << 6  | bit(val, 3)  << 5  | bit(val, 2) << 4  |
-         bit(val, 1)  << 3  | bit(val, 5)  << 2;
-}
-
-static void write_itype(u8 *loc, u32 val) {
-  u32 mask = 0b000000'00000'11111'111'11111'1111111;
-  *(ul32 *)loc = (*(ul32 *)loc & mask) | itype(val);
-}
-
-static void write_stype(u8 *loc, u32 val) {
-  u32 mask = 0b000000'11111'11111'111'00000'1111111;
-  *(ul32 *)loc = (*(ul32 *)loc & mask) | stype(val);
-}
-
-static void write_btype(u8 *loc, u32 val) {
-  u32 mask = 0b000000'11111'11111'111'00000'1111111;
-  *(ul32 *)loc = (*(ul32 *)loc & mask) | btype(val);
-}
-
-static void write_utype(u8 *loc, u32 val) {
-  u32 mask = 0b000000'00000'00000'000'11111'1111111;
-  *(ul32 *)loc = (*(ul32 *)loc & mask) | utype(val);
-}
-
-static void write_jtype(u8 *loc, u32 val) {
-  u32 mask = 0b000000'00000'00000'000'11111'1111111;
-  *(ul32 *)loc = (*(ul32 *)loc & mask) | jtype(val);
-}
-
-static void write_cbtype(u8 *loc, u32 val) {
-  u32 mask = 0b1110001110000011;
-  *(ul16 *)loc = (*(ul16 *)loc & mask) | cbtype(val);
-}
-
-static void write_cjtype(u8 *loc, u32 val) {
-  u32 mask = 0b1110000000000011;
-  *(ul16 *)loc = (*(ul16 *)loc & mask) | cjtype(val);
-}
-
-static void write_plt_header(Context<E> &ctx) {
-  u8 *buf = ctx.buf + ctx.plt->shdr.sh_offset;
-
-  static const u32 plt0[] = {
-    0x00000397, // auipc  t2, %pcrel_hi(.got.plt)
-    0x41c30333, // sub    t1, t1, t3               # .plt entry + hdr + 12
-    0x0003be03, // ld     t3, %pcrel_lo(1b)(t2)    # _dl_runtime_resolve
-    0xfd430313, // addi   t1, t1, -44              # .plt entry
-    0x00038293, // addi   t0, t2, %pcrel_lo(1b)    # &.got.plt
-    0x00135313, // srli   t1, t1, 1                # .plt entry offset
-    0x0082b283, // ld     t0, 8(t0)                # link map
-    0x000e0067, // jr     t3
-  };
-
-  u64 gotplt = ctx.gotplt->shdr.sh_addr;
-  u64 plt = ctx.plt->shdr.sh_addr;
-
-  memcpy(buf, plt0, sizeof(plt0));
-  write_utype(buf, gotplt - plt);
-  write_itype(buf + 8, gotplt - plt);
-  write_itype(buf + 16, gotplt - plt);
-}
-
-static void write_plt_entry(Context<E> &ctx, Symbol<E> &sym) {
-  u8 *ent = ctx.buf + ctx.plt->shdr.sh_offset + ctx.plt_hdr_size +
-            sym.get_plt_idx(ctx) * ctx.plt_size;
-
-  static const u32 data[] = {
-    0x00000e17, // auipc   t3, %pcrel_hi(function@.got.plt)
-    0x000e3e03, // ld      t3, %pcrel_lo(1b)(t3)
-    0x000e0367, // jalr    t1, t3
-    0x00000013, // nop
-  };
-
-  u64 gotplt = sym.get_gotplt_addr(ctx);
-  u64 plt = sym.get_plt_addr(ctx);
-
-  memcpy(ent, data, sizeof(data));
-  write_utype(ent, gotplt - plt);
-  write_itype(ent + 4, gotplt - plt);
-}
-
-template <>
-void PltSection<E>::copy_buf(Context<E> &ctx) {
-  write_plt_header(ctx);
-  for (Symbol<E> *sym : symbols)
-    write_plt_entry(ctx, *sym);
-}
-
-template <>
-void PltGotSection<E>::copy_buf(Context<E> &ctx) {
-  u8 *buf = ctx.buf + this->shdr.sh_offset;
-
-  static const u32 data[] = {
-    0x00000e17, // auipc   t3, %pcrel_hi(function@.got.plt)
-    0x000e3e03, // ld      t3, %pcrel_lo(1b)(t3)
-    0x000e0367, // jalr    t1, t3
-    0x00000013, // nop
-  };
-
-  for (Symbol<E> *sym : symbols) {
-    u8 *ent = buf + sym->get_pltgot_idx(ctx) * 16;
-    u64 got = sym->get_got_addr(ctx);
-    u64 plt = sym->get_plt_addr(ctx);
-
-    memcpy(ent, data, sizeof(data));
-    write_utype(ent, got - plt);
-    write_itype(ent + 4, got - plt);
-  }
-}
-
-template <>
-void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const ElfRel<E> &rel,
-                                    u64 offset, u64 val) {
-  u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
-
-  switch (rel.r_type) {
-  case R_RISCV_ADD32:
-    *(ul32 *)loc += val;
-    return;
-  case R_RISCV_SUB8:
-    *loc -= val;
-    return;
-  case R_RISCV_SUB16:
-    *(ul16 *)loc -= val;
-    return;
-  case R_RISCV_SUB32:
-    *(ul32 *)loc -= val;
-    return;
-  case R_RISCV_SUB6:
-    *loc = (*loc & 0b1100'0000) | ((*loc - val) & 0b0011'1111);
-    return;
-  case R_RISCV_SET6:
-    *loc = (*loc & 0b1100'0000) | (val & 0b0011'1111);
-    return;
-  case R_RISCV_SET8:
-    *loc = val;
-    return;
-  case R_RISCV_SET16:
-    *(ul16 *)loc = val;
-    return;
-  case R_RISCV_32_PCREL:
-    *(ul32 *)loc = val - this->shdr.sh_addr - offset;
-    return;
-  }
-  Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
-}
-
-template <>
-void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
-  ElfRel<E> *dynrel = nullptr;
-  std::span<const ElfRel<E>> rels = get_rels(ctx);
-
-  i64 frag_idx = 0;
-
-  if (ctx.reldyn)
-    dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
-                           file.reldyn_offset + this->reldyn_offset);
-
-  for (i64 i = 0; i < rels.size(); i++) {
-    const ElfRel<E> &rel = rels[i];
-    if (rel.r_type == R_RISCV_NONE || rel.r_type == R_RISCV_RELAX ||
-        rel.r_type == R_RISCV_ALIGN)
-      continue;
-
-    Symbol<E> &sym = *file.symbols[rel.r_sym];
-    i64 r_offset = rel.r_offset + extra.r_deltas[i];
-    u8 *loc = base + r_offset;
-
-    const SectionFragmentRef<E> *frag_ref = nullptr;
-    if (rel_fragments && rel_fragments[frag_idx].idx == i)
-      frag_ref = &rel_fragments[frag_idx++];
-
-#define S   (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
-#define A   (frag_ref ? (u64)frag_ref->addend : (u64)rel.r_addend)
-#define P   (output_section->shdr.sh_addr + offset + r_offset)
-#define G   (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
-#define GOT ctx.got->shdr.sh_addr
-
-    switch (rel.r_type) {
-    case R_RISCV_32:
-      *(ul32 *)loc = S + A;
-      break;
-    case R_RISCV_64:
-      if (sym.is_absolute() || !ctx.arg.pic) {
-        *(ul64 *)loc = S + A;
-      } else if (sym.is_imported) {
-        *dynrel++ = {P, R_RISCV_64, (u32)sym.get_dynsym_idx(ctx), A};
-        *(ul64 *)loc = A;
-      } else {
-        if (!is_relr_reloc(ctx, rel))
-          *dynrel++ = {P, R_RISCV_RELATIVE, 0, (i64)(S + A)};
-        *(ul64 *)loc = S + A;
-      }
-      break;
-    case R_RISCV_TLS_DTPMOD32:
-    case R_RISCV_TLS_DTPMOD64:
-    case R_RISCV_TLS_DTPREL32:
-    case R_RISCV_TLS_DTPREL64:
-    case R_RISCV_TLS_TPREL32:
-    case R_RISCV_TLS_TPREL64:
-      Error(ctx) << *this << ": unsupported relocation: " << rel;
-      break;
-    case R_RISCV_BRANCH:
-      write_btype(loc, S + A - P);
-      break;
-    case R_RISCV_JAL:
-      write_jtype(loc, S + A - P);
-      break;
-    case R_RISCV_CALL:
-    case R_RISCV_CALL_PLT: {
-      if (extra.r_deltas[i + 1] - extra.r_deltas[i] != 0) {
-        // auipc + jalr -> jal
-        assert(extra.r_deltas[i + 1] - extra.r_deltas[i] == -4);
-        u32 jalr = *(ul32 *)&contents[rels[i].r_offset + 4];
-        *(ul32 *)loc = (0b11111'000000 & jalr) | 0b101111;
-        write_jtype(loc, S + A - P);
-      } else {
-        u64 val = sym.esym().is_undef_weak() ? 0 : S + A - P;
-        write_utype(loc, val);
-        write_itype(loc + 4, val);
-      }
-      break;
-    }
-    case R_RISCV_GOT_HI20:
-      *(ul32 *)loc = G + GOT + A - P;
-      break;
-    case R_RISCV_TLS_GOT_HI20:
-      *(ul32 *)loc = sym.get_gottp_addr(ctx) + A - P;
-      break;
-    case R_RISCV_TLS_GD_HI20:
-      *(ul32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
-      break;
-    case R_RISCV_PCREL_HI20:
-      if (sym.esym().is_undef_weak()) {
-        // Calling an undefined weak symbol does not make sense.
-        // We make such call into an infinite loop. This should
-        // help debugging of a faulty program.
-        *(ul32 *)loc = P;
-      } else {
-        *(ul32 *)loc = S + A - P;
-      }
-      break;
-    case R_RISCV_PCREL_LO12_I:
-      assert(sym.get_input_section() == this);
-      assert(sym.value < r_offset);
-      write_itype(loc, *(ul32 *)(base + sym.value));
-      break;
-    case R_RISCV_LO12_I:
-    case R_RISCV_TPREL_LO12_I:
-      write_itype(loc, S + A);
-      break;
-    case R_RISCV_PCREL_LO12_S:
-      assert(sym.get_input_section() == this);
-      assert(sym.value < r_offset);
-      write_stype(loc, *(ul32 *)(base + sym.value));
-      break;
-    case R_RISCV_LO12_S:
-    case R_RISCV_TPREL_LO12_S:
-      write_stype(loc, S + A);
-      break;
-    case R_RISCV_HI20:
-      write_utype(loc, S + A);
-      break;
-    case R_RISCV_TPREL_HI20:
-      write_utype(loc, S + A - ctx.tls_begin);
-      break;
-    case R_RISCV_TPREL_ADD:
-      break;
-    case R_RISCV_ADD8:
-      loc += S + A;
-      break;
-    case R_RISCV_ADD16:
-      *(ul16 *)loc += S + A;
-      break;
-    case R_RISCV_ADD32:
-      *(ul32 *)loc += S + A;
-      break;
-    case R_RISCV_ADD64:
-      *(ul64 *)loc += S + A;
-      break;
-    case R_RISCV_SUB8:
-      loc -= S + A;
-      break;
-    case R_RISCV_SUB16:
-      *(ul16 *)loc -= S + A;
-      break;
-    case R_RISCV_SUB32:
-      *(ul32 *)loc -= S + A;
-      break;
-    case R_RISCV_SUB64:
-      *(ul64 *)loc -= S + A;
-      break;
-    case R_RISCV_RVC_BRANCH:
-      write_cbtype(loc, S + A - P);
-      break;
-    case R_RISCV_RVC_JUMP:
-      write_cjtype(loc, S + A - P);
-      break;
-    case R_RISCV_RVC_LUI:
-      Error(ctx) << *this << ": unsupported relocation: " << rel;
-      break;
-    case R_RISCV_SUB6:
-      *loc = (*loc & 0b1100'0000) | ((*loc - (S + A)) & 0b0011'1111);
-      break;
-    case R_RISCV_SET6:
-      *loc = (*loc & 0b1100'0000) | ((S + A) & 0b0011'1111);
-      break;
-    case R_RISCV_SET8:
-      *loc = S + A;
-      break;
-    case R_RISCV_SET16:
-      *(ul16 *)loc = S + A;
-      break;
-    case R_RISCV_SET32:
-      *(ul32 *)loc = S + A;
-      break;
-    case R_RISCV_32_PCREL:
-      *(ul32 *)loc = S + A - P;
-      break;
-    default:
-      Error(ctx) << *this << ": unknown relocation: " << rel;
-    }
-
-#undef S
-#undef A
-#undef P
-#undef G
-#undef GOT
-  }
-
-  // In the above loop, PC-relative HI20 relocations overwrote
-  // instructions with full 32-bit values to allow their corresponding
-  // PCREL_LO12 relocations to read their values. This loop restore
-  // the original instructions.
-  for (i64 i = 0; i < rels.size(); i++) {
-    switch (rels[i].r_type) {
-    case R_RISCV_GOT_HI20:
-    case R_RISCV_PCREL_HI20:
-    case R_RISCV_TLS_GOT_HI20:
-    case R_RISCV_TLS_GD_HI20: {
-      u8 *loc = base + rels[i].r_offset + extra.r_deltas[i];
-      u32 val = *(ul32 *)loc;
-      *(ul32 *)loc = *(ul32 *)&contents[rels[i].r_offset];
-      write_utype(loc, val);
-    }
-    }
-  }
-}
-
-template <>
-void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
-  std::span<const ElfRel<E>> rels = get_rels(ctx);
-
-  for (i64 i = 0; i < rels.size(); i++) {
-    const ElfRel<E> &rel = rels[i];
-    if (rel.r_type == R_RISCV_NONE)
-      continue;
-
-    Symbol<E> &sym = *file.symbols[rel.r_sym];
-    u8 *loc = base + rel.r_offset;
-
-    if (!sym.file) {
-      record_undef_error(ctx, rel);
-      continue;
-    }
-
-    SectionFragment<E> *frag;
-    i64 addend;
-    std::tie(frag, addend) = get_fragment(ctx, rel);
-
-#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
-#define A (frag ? (u64)addend : (u64)rel.r_addend)
-
-    switch (rel.r_type) {
-    case R_RISCV_32:
-      *(ul32 *)loc = S + A;
-      break;
-    case R_RISCV_64:
-      if (!frag) {
-        if (std::optional<u64> val = get_tombstone(sym)) {
-          *(ul64 *)loc = *val;
-          break;
-        }
-      }
-      *(ul64 *)loc = S + A;
-      break;
-    case R_RISCV_ADD8:
-      *loc += S + A;
-      break;
-    case R_RISCV_ADD16:
-      *(ul16 *)loc += S + A;
-      break;
-    case R_RISCV_ADD32:
-      *(ul32 *)loc += S + A;
-      break;
-    case R_RISCV_ADD64:
-      *(ul64 *)loc += S + A;
-      break;
-    case R_RISCV_SUB8:
-      *loc -= S + A;
-      break;
-    case R_RISCV_SUB16:
-      *(ul16 *)loc -= S + A;
-      break;
-    case R_RISCV_SUB32:
-      *(ul32 *)loc -= S + A;
-      break;
-    case R_RISCV_SUB64:
-      *(ul64 *)loc -= S + A;
-      break;
-    case R_RISCV_SUB6:
-      *loc = (*loc & 0b1100'0000) | ((*loc - (S + A)) & 0b0011'1111);
-      break;
-    case R_RISCV_SET6:
-      *loc = (*loc & 0b1100'0000) | ((S + A) & 0b0011'1111);
-      break;
-    case R_RISCV_SET8:
-      *loc = S + A;
-      break;
-    case R_RISCV_SET16:
-      *(ul16 *)loc = S + A;
-      break;
-    case R_RISCV_SET32:
-      *(ul32 *)loc = S + A;
-      break;
-    default:
-      Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
-                 << rel;
-      break;
-    }
-
-#undef S
-#undef A
-  }
-}
-
-template <>
-void InputSection<E>::copy_contents_riscv(Context<E> &ctx, u8 *buf) {
-  // A non-alloc section isn't relaxed, so just copy it as one big chunk.
-  if (!(shdr().sh_flags & SHF_ALLOC)) {
-    if (compressed)
-      uncompress_to(ctx, buf);
-    else
-      memcpy(buf, contents.data(), contents.size());
-    return;
-  }
-
-  // Memory-allocated sections may be relaxed, so copy each segment
-  // individually.
-  std::span<const ElfRel<E>> rels = get_rels(ctx);
-  i64 pos = 0;
-
-  for (i64 i = 0; i < rels.size(); i++) {
-    i64 delta = extra.r_deltas[i + 1] - extra.r_deltas[i];
-    if (delta == 0)
-      continue;
-    assert(delta < 0);
-
-    const ElfRel<E> &r = rels[i];
-    memcpy(buf, contents.data() + pos, r.r_offset - pos);
-    buf += r.r_offset - pos;
-    pos = r.r_offset - delta;
-  }
-
-  memcpy(buf, contents.data() + pos, contents.size() - pos);
-}
-
-template <>
-void InputSection<E>::scan_relocations(Context<E> &ctx) {
-  assert(shdr().sh_flags & SHF_ALLOC);
-
-  this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
-  std::span<const ElfRel<E>> rels = get_rels(ctx);
-
-  // Scan relocations
-  for (i64 i = 0; i < rels.size(); i++) {
-    const ElfRel<E> &rel = rels[i];
-    if (rel.r_type == R_RISCV_NONE)
-      continue;
-
-    Symbol<E> &sym = *file.symbols[rel.r_sym];
-
-    if (!sym.file) {
-      record_undef_error(ctx, rel);
-      continue;
-    }
-
-    if (sym.get_type() == STT_GNU_IFUNC) {
-      sym.flags |= NEEDS_GOT;
-      sym.flags |= NEEDS_PLT;
-    }
-
-    switch (rel.r_type) {
-    case R_RISCV_32:
-    case R_RISCV_HI20: {
-      Action table[][4] = {
-        // Absolute  Local    Imported data  Imported code
-        {  NONE,     ERROR,   ERROR,         ERROR },      // DSO
-        {  NONE,     ERROR,   ERROR,         ERROR },      // PIE
-        {  NONE,     NONE,    COPYREL,       CPLT  },      // PDE
-      };
-      dispatch(ctx, table, i, rel, sym);
-      break;
-    }
-    case R_RISCV_64: {
-      Action table[][4] = {
-        // Absolute  Local    Imported data  Imported code
-        {  NONE,     BASEREL, DYNREL,        DYNREL },     // DSO
-        {  NONE,     BASEREL, DYNREL,        DYNREL },     // PIE
-        {  NONE,     NONE,    COPYREL,       CPLT   },     // PDE
-      };
-      dispatch(ctx, table, i, rel, sym);
-      break;
-    }
-    case R_RISCV_TLS_DTPMOD32:
-    case R_RISCV_TLS_DTPMOD64:
-    case R_RISCV_TLS_DTPREL32:
-    case R_RISCV_TLS_DTPREL64:
-    case R_RISCV_TLS_TPREL32:
-    case R_RISCV_TLS_TPREL64:
-      Error(ctx) << *this << ": unsupported relocation: " << rel;
-      break;
-    case R_RISCV_BRANCH:
-    case R_RISCV_JAL:
-      break;
-    case R_RISCV_CALL:
-    case R_RISCV_CALL_PLT:
-      if (sym.is_imported)
-        sym.flags |= NEEDS_PLT;
-      break;
-    case R_RISCV_GOT_HI20:
-      sym.flags |= NEEDS_GOT;
-      break;
-    case R_RISCV_TLS_GOT_HI20:
-      ctx.has_gottp_rel = true;
-      sym.flags |= NEEDS_GOTTP;
-      break;
-    case R_RISCV_TLS_GD_HI20:
-      sym.flags |= NEEDS_TLSGD;
-      break;
-    case R_RISCV_PCREL_HI20:
-    case R_RISCV_PCREL_LO12_I:
-    case R_RISCV_PCREL_LO12_S:
-    case R_RISCV_LO12_I:
-    case R_RISCV_LO12_S:
-    case R_RISCV_TPREL_HI20:
-    case R_RISCV_TPREL_LO12_I:
-    case R_RISCV_TPREL_LO12_S:
-    case R_RISCV_TPREL_ADD:
-    case R_RISCV_ADD8:
-    case R_RISCV_ADD16:
-    case R_RISCV_ADD32:
-    case R_RISCV_ADD64:
-    case R_RISCV_SUB8:
-    case R_RISCV_SUB16:
-    case R_RISCV_SUB32:
-    case R_RISCV_SUB64:
-    case R_RISCV_ALIGN:
-      break;
-    case R_RISCV_RVC_BRANCH:
-    case R_RISCV_RVC_JUMP:
-      break;
-    case R_RISCV_RVC_LUI:
-      Error(ctx) << *this << ": unsupported relocation: " << rel;
-      break;
-    case R_RISCV_RELAX:
-    case R_RISCV_SUB6:
-    case R_RISCV_SET6:
-    case R_RISCV_SET8:
-    case R_RISCV_SET16:
-    case R_RISCV_SET32:
-      break;
-    case R_RISCV_32_PCREL: {
-      Action table[][4] = {
-        // Absolute  Local  Imported data  Imported code
-        {  ERROR,    NONE,  ERROR,         ERROR },      // DSO
-        {  ERROR,    NONE,  COPYREL,       PLT   },      // PIE
-        {  NONE,     NONE,  COPYREL,       PLT   },      // PDE
-      };
-      dispatch(ctx, table, i, rel, sym);
-      break;
-    }
-    default:
-      Error(ctx) << *this << ": unknown relocation: " << rel;
-    }
-  }
-}
-
-static bool is_resizable(Context<E> &ctx, InputSection<E> *isec) {
-  return isec && (isec->shdr().sh_flags & SHF_ALLOC);
-}
-
-// Initializes sorted_symbols.
-static void sort_symbols(Context<E> &ctx) {
-  tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
-    for (Symbol<E> *sym : file->symbols)
-      if (sym->file == file)
-        if (InputSection<E> *isec = sym->get_input_section())
-          isec->extra.sorted_symbols.push_back(sym);
-
-    for (std::unique_ptr<InputSection<E>> &isec : file->sections)
-      if (isec)
-        sort(isec->extra.sorted_symbols,
-             [](Symbol<E> *a, Symbol<E> *b) { return a->value < b->value; });
-  });
-}
-
-// Returns the distance between a relocated place and a symbol.
-static i64 compute_distance(Context<E> &ctx, Symbol<E> &sym,
-                            InputSection<E> &isec, const ElfRel<E> &rel) {
-  // We handle absolute symbols as if they were infinitely far away
-  // because `relax_section` may increase a distance between a branch
-  // instruction and an absolute symbol. Branching to an absolute
-  // location is extremely rare in real code, though.
-  if (sym.is_absolute())
-    return INT32_MAX;
-
-  // Likewise, relocations against weak undefined symbols won't be relaxed.
-  if (sym.esym().is_undef_weak())
-    return INT32_MAX;
-
-  // Compute a distance between the relocated place and the symbol.
-  i64 S = sym.get_addr(ctx);
-  i64 A = rel.r_addend;
-  i64 P = isec.get_addr() + rel.r_offset;
-  return S + A - P;
-}
-
-// Relax R_RISCV_CALL and R_RISCV_CALL_PLT relocations.
-static void relax_section(Context<E> &ctx, InputSection<E> &isec) {
-  std::span<Symbol<E> *> syms = isec.extra.sorted_symbols;
-  i64 delta = 0;
-
-  std::span<const ElfRel<E>> rels = isec.get_rels(ctx);
-  isec.extra.r_deltas.resize(rels.size() + 1);
-
-  for (i64 i = 0; i < rels.size(); i++) {
-    const ElfRel<E> &r = rels[i];
-    i64 delta2 = 0;
-
-    isec.extra.r_deltas[i] += delta;
-
-    switch (r.r_type) {
-    case R_RISCV_ALIGN: {
-      // R_RISCV_ALIGN refers NOP instructions. We need to eliminate
-      // some or all of the instructions so that the instruction that
-      // immediately follows the NOPs is aligned to a specified
-      // alignment boundary.
-      u64 loc = isec.get_addr() + r.r_offset + delta;
-
-      // The total bytes of NOPs is stored to r_addend, so the next
-      // instruction is r_addend away.
-      u64 next_loc = loc + r.r_addend;
-      u64 alignment = bit_ceil(r.r_addend);
-      if (next_loc % alignment)
-        delta2 = align_to(loc, alignment) - next_loc;
-      break;
-    }
-    case R_RISCV_CALL:
-    case R_RISCV_CALL_PLT:
-      if (ctx.arg.relax) {
-        if (i == rels.size() - 1 || rels[i + 1].r_type != R_RISCV_RELAX)
-          break;
-
-        // If the jump target is within ±1 MiB, we can replace AUIPC+JALR
-        // with JAL, saving 4 bytes.
-        Symbol<E> &sym = *isec.file.symbols[r.r_sym];
-        i64 dist = compute_distance(ctx, sym, isec, r);
-        if (dist % 2 == 0 && -(1 << 20) <= dist && dist < (1 << 20))
-          delta2 = -4;
-      }
-    }
-
-    if (delta2 == 0)
-      continue;
-
-    while (!syms.empty() && syms[0]->value <= r.r_offset) {
-      syms[0]->value += delta;
-      syms = syms.subspan(1);
-    }
-
-    delta += delta2;
-  }
-
-  for (Symbol<E> *sym : syms)
-    sym->value += delta;
-  isec.extra.r_deltas[rels.size()] += delta;
-
-  isec.sh_size += delta;
-}
-
-// RISC-V instructions are 16 or 32 bits long, so immediates encoded
-// in instructions can't be 32 bits long. Therefore, branch and load
-// instructions can't refer the 4 GiB address space unlike x86-64.
-// In fact, JAL (jump and link) instruction can jump to only within
-// ±1 MiB as their immediate is only 21 bits long.
-//
-// If you want to jump to somewhere further than that, you need to
-// construct a full 32-bit offset using multiple instruction and
-// branch to that place (e.g. AUIPC and JALR instead of JAL).
-// In this comment, we refer instructions such as JAL as the short
-// encoding and ones such as AUIPC+JALR as the long encoding.
-//
-// By default, compiler always uses the long encoding so that branch
-// targets are always encodable. This is a safe bet for them but
-// may result in inefficient code. Therefore, the RISC-V psABI defines
-// a mechanism for the linker to replace long encoding instructions
-// with short ones, shrinking the section and increasing the code
-// density.
-//
-// This is contrary to the psABIs for the other RISC processors such as
-// ARM64. Typically, they use short instructions by default, and a
-// linker creates so-called "thunks" to extend ranges of short jumps.
-// On RISC-V, instructions are in the long encoding by default, and
-// the linker shrinks them if it can.
-//
-// When we shrink a section, we need to adjust relocation offsets and
-// symbol values. For example, if we replace AUIPC+JALR with JAL
-// (which saves 4 bytes), all relocations pointing to anywhere after
-// that location need to be shifted by 4. In addition to that, any
-// symbol that refers anywhere after that locatioin need to be shifted
-// by 4 bytes as well.
-//
-// For relocations, we use `r_deltas` array to memorize how many bytes
-// have be adjusted. For symbols, we directly mutate their `value`
-// member.
-//
-// This operation seems to be optional, as by default instructions are
-// using the long encoding, but calling this function is actually
-// mandatory because of R_RISCV_ALIGN. R_RISCV_ALIGN relocation is a
-// directive to the linker to align the location referred to by the
-// relocation to a specified byte boundary. We at least have to
-// interpret them satisfy the constraints imposed by R_RISCV_ALIGN
-// relocations.
-i64 riscv_resize_sections(Context<E> &ctx) {
-  Timer t(ctx, "riscv_resize_sections");
-  sort_symbols(ctx);
-
-  // Find R_RISCV_CALL AND R_RISCV_CALL_PLT that can be relaxed.
-  // This step should only shrink sections.
-  tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
-    for (std::unique_ptr<InputSection<E>> &isec : file->sections)
-      if (is_resizable(ctx, isec.get()))
-        relax_section(ctx, *isec);
-  });
-
-  // Re-compute section offset again to finalize them.
-  compute_section_sizes(ctx);
-  return set_osec_offsets(ctx);
-}
-
-} // namespace mold::elf
diff -pruN 1.3.1+dfsg-1/elf/arch-riscv.cc 1.4.0+dfsg-1/elf/arch-riscv.cc
--- 1.3.1+dfsg-1/elf/arch-riscv.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/elf/arch-riscv.cc	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,833 @@
+#include "mold.h"
+
+#include <tbb/parallel_for.h>
+#include <tbb/parallel_for_each.h>
+
+namespace mold::elf {
+
+static u32 itype(u32 val) {
+  return val << 20;
+}
+
+static u32 stype(u32 val) {
+  return bits(val, 11, 5) << 25 | bits(val, 4, 0) << 7;
+}
+
+static u32 btype(u32 val) {
+  return bit(val, 12) << 31 | bits(val, 10, 5) << 25 |
+         bits(val, 4, 1) << 8 | bit(val, 11) << 7;
+}
+
+static u32 utype(u32 val) {
+  // U-type instructions are used in combination with I-type
+  // instructions. U-type insn sets an immediate to the upper 20-bits
+  // of a register. I-type insn sign-extends a 12-bits immediate and
+  // add it to a register value to construct a complete value. 0x800
+  // is added here to compensate for the sign-extension.
+  return bits(val + 0x800, 31, 12) << 12;
+}
+
+static u32 jtype(u32 val) {
+  return bit(val, 20) << 31 | bits(val, 10, 1)  << 21 |
+         bit(val, 11) << 20 | bits(val, 19, 12) << 12;
+}
+
+static u32 cbtype(u32 val) {
+  return bit(val, 8) << 12 | bit(val, 4) << 11 | bit(val, 3) << 10 |
+         bit(val, 7) << 6  | bit(val, 6) << 5  | bit(val, 2) << 4  |
+         bit(val, 1) << 3  | bit(val, 5) << 2;
+}
+
+static u32 cjtype(u32 val) {
+  return bit(val, 11) << 12 | bit(val, 4)  << 11 | bit(val, 9) << 10 |
+         bit(val, 8)  << 9  | bit(val, 10) << 8  | bit(val, 6) << 7  |
+         bit(val, 7)  << 6  | bit(val, 3)  << 5  | bit(val, 2) << 4  |
+         bit(val, 1)  << 3  | bit(val, 5)  << 2;
+}
+
+static void write_itype(u8 *loc, u32 val) {
+  u32 mask = 0b000000'00000'11111'111'11111'1111111;
+  *(ul32 *)loc = (*(ul32 *)loc & mask) | itype(val);
+}
+
+static void write_stype(u8 *loc, u32 val) {
+  u32 mask = 0b000000'11111'11111'111'00000'1111111;
+  *(ul32 *)loc = (*(ul32 *)loc & mask) | stype(val);
+}
+
+static void write_btype(u8 *loc, u32 val) {
+  u32 mask = 0b000000'11111'11111'111'00000'1111111;
+  *(ul32 *)loc = (*(ul32 *)loc & mask) | btype(val);
+}
+
+static void write_utype(u8 *loc, u32 val) {
+  u32 mask = 0b000000'00000'00000'000'11111'1111111;
+  *(ul32 *)loc = (*(ul32 *)loc & mask) | utype(val);
+}
+
+static void write_jtype(u8 *loc, u32 val) {
+  u32 mask = 0b000000'00000'00000'000'11111'1111111;
+  *(ul32 *)loc = (*(ul32 *)loc & mask) | jtype(val);
+}
+
+static void write_cbtype(u8 *loc, u32 val) {
+  u32 mask = 0b1110001110000011;
+  *(ul16 *)loc = (*(ul16 *)loc & mask) | cbtype(val);
+}
+
+static void write_cjtype(u8 *loc, u32 val) {
+  u32 mask = 0b1110000000000011;
+  *(ul16 *)loc = (*(ul16 *)loc & mask) | cjtype(val);
+}
+
+template <typename E>
+static void write_plt_header(Context<E> &ctx) {
+  u8 *buf = ctx.buf + ctx.plt->shdr.sh_offset;
+
+  static const u32 plt0_64[] = {
+    0x00000397, // auipc  t2, %pcrel_hi(.got.plt)
+    0x41c30333, // sub    t1, t1, t3               # .plt entry + hdr + 12
+    0x0003be03, // ld     t3, %pcrel_lo(1b)(t2)    # _dl_runtime_resolve
+    0xfd430313, // addi   t1, t1, -44              # .plt entry
+    0x00038293, // addi   t0, t2, %pcrel_lo(1b)    # &.got.plt
+    0x00135313, // srli   t1, t1, 1                # .plt entry offset
+    0x0082b283, // ld     t0, 8(t0)                # link map
+    0x000e0067, // jr     t3
+  };
+
+  static const u32 plt0_32[] = {
+    0x00000397, // auipc  t2, %pcrel_hi(.got.plt)
+    0x41c30333, // sub    t1, t1, t3               # .plt entry + hdr + 12
+    0x0003ae03, // lw     t3, %pcrel_lo(1b)(t2)    # _dl_runtime_resolve
+    0xfd430313, // addi   t1, t1, -44              # .plt entry
+    0x00038293, // addi   t0, t2, %pcrel_lo(1b)    # &.got.plt
+    0x00135313, // srli   t1, t1, 1                # .plt entry offset
+    0x0082a283, // lw     t0, 8(t0)                # link map
+    0x000e0067, // jr     t3
+  };
+
+  if constexpr (E::word_size == 8)
+    memcpy(buf, plt0_64, sizeof(plt0_64));
+  else
+    memcpy(buf, plt0_32, sizeof(plt0_32));
+
+  u64 gotplt = ctx.gotplt->shdr.sh_addr;
+  u64 plt = ctx.plt->shdr.sh_addr;
+
+  write_utype(buf, gotplt - plt);
+  write_itype(buf + 8, gotplt - plt);
+  write_itype(buf + 16, gotplt - plt);
+}
+
+static constexpr u32 plt_entry_64[] = {
+  0x00000e17, // auipc   t3, %pcrel_hi(function@.got.plt)
+  0x000e3e03, // ld      t3, %pcrel_lo(1b)(t3)
+  0x000e0367, // jalr    t1, t3
+  0x00000013, // nop
+};
+
+static constexpr u32 plt_entry_32[] = {
+  0x00000e17, // auipc   t3, %pcrel_hi(function@.got.plt)
+  0x000e2e03, // lw      t3, %pcrel_lo(1b)(t3)
+  0x000e0367, // jalr    t1, t3
+  0x00000013, // nop
+};
+
+template <typename E>
+void PltSection<E>::copy_buf(Context<E> &ctx) {
+  u8 *buf = ctx.buf + ctx.plt->shdr.sh_offset;
+
+  write_plt_header(ctx);
+
+  for (Symbol<E> *sym : symbols) {
+    u8 *ent = buf + E::plt_hdr_size + sym->get_plt_idx(ctx) * E::plt_size;
+    u64 gotplt = sym->get_gotplt_addr(ctx);
+    u64 plt = sym->get_plt_addr(ctx);
+
+    if constexpr (E::word_size == 8)
+      memcpy(ent, plt_entry_64, sizeof(plt_entry_64));
+    else
+      memcpy(ent, plt_entry_32, sizeof(plt_entry_32));
+
+    write_utype(ent, gotplt - plt);
+    write_itype(ent + 4, gotplt - plt);
+  }
+}
+
+template <typename E>
+void PltGotSection<E>::copy_buf(Context<E> &ctx) {
+  u8 *buf = ctx.buf + this->shdr.sh_offset;
+
+  for (Symbol<E> *sym : symbols) {
+    u8 *ent = buf + sym->get_pltgot_idx(ctx) * E::pltgot_size;
+    u64 got = sym->get_got_addr(ctx);
+    u64 plt = sym->get_plt_addr(ctx);
+
+    if constexpr (E::word_size == 8)
+      memcpy(ent, plt_entry_64, sizeof(plt_entry_64));
+    else
+      memcpy(ent, plt_entry_32, sizeof(plt_entry_32));
+
+    write_utype(ent, got - plt);
+    write_itype(ent + 4, got - plt);
+  }
+}
+
+template <typename E>
+void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const ElfRel<E> &rel,
+                                    u64 offset, u64 val) {
+  u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
+
+  switch (rel.r_type) {
+  case R_RISCV_ADD32:
+    *(ul32 *)loc += val;
+    return;
+  case R_RISCV_SUB8:
+    *loc -= val;
+    return;
+  case R_RISCV_SUB16:
+    *(ul16 *)loc -= val;
+    return;
+  case R_RISCV_SUB32:
+    *(ul32 *)loc -= val;
+    return;
+  case R_RISCV_SUB6:
+    *loc = (*loc & 0b1100'0000) | ((*loc - val) & 0b0011'1111);
+    return;
+  case R_RISCV_SET6:
+    *loc = (*loc & 0b1100'0000) | (val & 0b0011'1111);
+    return;
+  case R_RISCV_SET8:
+    *loc = val;
+    return;
+  case R_RISCV_SET16:
+    *(ul16 *)loc = val;
+    return;
+  case R_RISCV_SET32:
+    *(ul32 *)loc = val;
+    return;
+  case R_RISCV_32_PCREL:
+    *(ul32 *)loc = val - this->shdr.sh_addr - offset;
+    return;
+  }
+  Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
+}
+
+template <typename E>
+void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
+  ElfRel<E> *dynrel = nullptr;
+  std::span<const ElfRel<E>> rels = get_rels(ctx);
+
+  i64 frag_idx = 0;
+
+  if (ctx.reldyn)
+    dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
+                           file.reldyn_offset + this->reldyn_offset);
+
+  for (i64 i = 0; i < rels.size(); i++) {
+    const ElfRel<E> &rel = rels[i];
+    if (rel.r_type == R_RISCV_NONE || rel.r_type == R_RISCV_RELAX)
+      continue;
+
+    Symbol<E> &sym = *file.symbols[rel.r_sym];
+    i64 r_offset = rel.r_offset + extra.r_deltas[i];
+    u8 *loc = base + r_offset;
+
+    const SectionFragmentRef<E> *frag_ref = nullptr;
+    if (rel_fragments && rel_fragments[frag_idx].idx == i)
+      frag_ref = &rel_fragments[frag_idx++];
+
+#define S   (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
+#define A   (frag_ref ? (u64)frag_ref->addend : (u64)rel.r_addend)
+#define P   (output_section->shdr.sh_addr + offset + r_offset)
+#define G   (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
+#define GOT ctx.got->shdr.sh_addr
+
+    switch (rel.r_type) {
+    case R_RISCV_32:
+      *(ul32 *)loc = S + A;
+      break;
+    case R_RISCV_64:
+      if (sym.is_absolute() || !ctx.arg.pic) {
+        *(ul64 *)loc = S + A;
+      } else if (sym.is_imported) {
+        *dynrel++ = {P, R_RISCV_64, (u32)sym.get_dynsym_idx(ctx), A};
+        *(ul64 *)loc = A;
+      } else {
+        if (!is_relr_reloc(ctx, rel))
+          *dynrel++ = {P, R_RISCV_RELATIVE, 0, (i64)(S + A)};
+        *(ul64 *)loc = S + A;
+      }
+      break;
+    case R_RISCV_BRANCH:
+      write_btype(loc, S + A - P);
+      break;
+    case R_RISCV_JAL:
+      write_jtype(loc, S + A - P);
+      break;
+    case R_RISCV_CALL:
+    case R_RISCV_CALL_PLT: {
+      if (extra.r_deltas[i + 1] - extra.r_deltas[i] != 0) {
+        // auipc + jalr -> jal
+        assert(extra.r_deltas[i + 1] - extra.r_deltas[i] == -4);
+        u32 jalr = *(ul32 *)&contents[rels[i].r_offset + 4];
+        *(ul32 *)loc = (0b11111'000000 & jalr) | 0b101111;
+        write_jtype(loc, S + A - P);
+      } else {
+        u64 val = sym.esym().is_undef_weak() ? 0 : S + A - P;
+        write_utype(loc, val);
+        write_itype(loc + 4, val);
+      }
+      break;
+    }
+    case R_RISCV_GOT_HI20:
+      *(ul32 *)loc = G + GOT + A - P;
+      break;
+    case R_RISCV_TLS_GOT_HI20:
+      *(ul32 *)loc = sym.get_gottp_addr(ctx) + A - P;
+      break;
+    case R_RISCV_TLS_GD_HI20:
+      *(ul32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
+      break;
+    case R_RISCV_PCREL_HI20:
+      if (sym.esym().is_undef_weak()) {
+        // Calling an undefined weak symbol does not make sense.
+        // We make such call into an infinite loop. This should
+        // help debugging of a faulty program.
+        *(ul32 *)loc = P;
+      } else {
+        *(ul32 *)loc = S + A - P;
+      }
+      break;
+    case R_RISCV_LO12_I:
+    case R_RISCV_TPREL_LO12_I:
+      write_itype(loc, S + A);
+      break;
+    case R_RISCV_LO12_S:
+    case R_RISCV_TPREL_LO12_S:
+      write_stype(loc, S + A);
+      break;
+    case R_RISCV_HI20:
+      write_utype(loc, S + A);
+      break;
+    case R_RISCV_TPREL_HI20:
+      write_utype(loc, S + A - ctx.tls_begin);
+      break;
+    case R_RISCV_TPREL_ADD:
+      break;
+    case R_RISCV_ADD8:
+      loc += S + A;
+      break;
+    case R_RISCV_ADD16:
+      *(ul16 *)loc += S + A;
+      break;
+    case R_RISCV_ADD32:
+      *(ul32 *)loc += S + A;
+      break;
+    case R_RISCV_ADD64:
+      *(ul64 *)loc += S + A;
+      break;
+    case R_RISCV_SUB8:
+      loc -= S + A;
+      break;
+    case R_RISCV_SUB16:
+      *(ul16 *)loc -= S + A;
+      break;
+    case R_RISCV_SUB32:
+      *(ul32 *)loc -= S + A;
+      break;
+    case R_RISCV_SUB64:
+      *(ul64 *)loc -= S + A;
+      break;
+    case R_RISCV_ALIGN:
+      // A R_RISCV_ALIGN is followed by nops. We sometimes have to not
+      // just remove nops but rewrite a nop with a c.nop. Here, we always
+      // rewrite all nops for the sake of simplicity.
+      if (i64 padding_size = align_to(P, bit_ceil(rel.r_addend)) - P) {
+        assert(padding_size % 2 == 0);
+        i64 i = 0;
+        for (; i <= padding_size - 4; i += 4)
+          *(ul32 *)(loc + i) = 0x00000013; // nop
+        if (i != padding_size)
+          *(ul16 *)(loc + i) = 0x0001;     // c.nop
+      }
+      break;
+    case R_RISCV_RVC_BRANCH:
+      write_cbtype(loc, S + A - P);
+      break;
+    case R_RISCV_RVC_JUMP:
+      write_cjtype(loc, S + A - P);
+      break;
+    case R_RISCV_SUB6:
+      *loc = (*loc & 0b1100'0000) | ((*loc - (S + A)) & 0b0011'1111);
+      break;
+    case R_RISCV_SET6:
+      *loc = (*loc & 0b1100'0000) | ((S + A) & 0b0011'1111);
+      break;
+    case R_RISCV_SET8:
+      *loc = S + A;
+      break;
+    case R_RISCV_SET16:
+      *(ul16 *)loc = S + A;
+      break;
+    case R_RISCV_SET32:
+      *(ul32 *)loc = S + A;
+      break;
+    case R_RISCV_32_PCREL:
+      *(ul32 *)loc = S + A - P;
+      break;
+    case R_RISCV_PCREL_LO12_I:
+    case R_RISCV_PCREL_LO12_S:
+      // These relocations are handled in the next loop.
+      break;
+    default:
+      unreachable();
+    }
+
+#undef S
+#undef A
+#undef P
+#undef G
+#undef GOT
+  }
+
+  // Handle LO12 relocations. In the above loop, PC-relative HI20
+  // relocations overwrote instructions with full 32-bit values to allow
+  // their corresponding LO12 relocations to read their values.
+  for (i64 i = 0; i < rels.size(); i++) {
+    const ElfRel<E> &r = rels[i];
+    if (r.r_type != R_RISCV_PCREL_LO12_I && r.r_type != R_RISCV_PCREL_LO12_S)
+      continue;
+
+    Symbol<E> &sym = *file.symbols[r.r_sym];
+    assert(sym.get_input_section() == this);
+
+    u8 *loc = base + r.r_offset + extra.r_deltas[i];
+    u32 val = *(ul32 *)(base + sym.value);
+
+    if (r.r_type == R_RISCV_PCREL_LO12_I)
+      write_itype(loc, val);
+    else
+      write_stype(loc, val);
+  }
+
+  // Restore the original instructions HI20 relocations overwrote.
+  for (i64 i = 0; i < rels.size(); i++) {
+    switch (rels[i].r_type) {
+    case R_RISCV_GOT_HI20:
+    case R_RISCV_PCREL_HI20:
+    case R_RISCV_TLS_GOT_HI20:
+    case R_RISCV_TLS_GD_HI20: {
+      u8 *loc = base + rels[i].r_offset + extra.r_deltas[i];
+      u32 val = *(ul32 *)loc;
+      *(ul32 *)loc = *(ul32 *)&contents[rels[i].r_offset];
+      write_utype(loc, val);
+    }
+    }
+  }
+}
+
+template <typename E>
+void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
+  std::span<const ElfRel<E>> rels = get_rels(ctx);
+
+  for (i64 i = 0; i < rels.size(); i++) {
+    const ElfRel<E> &rel = rels[i];
+    if (rel.r_type == R_RISCV_NONE)
+      continue;
+
+    Symbol<E> &sym = *file.symbols[rel.r_sym];
+    u8 *loc = base + rel.r_offset;
+
+    if (!sym.file) {
+      record_undef_error(ctx, rel);
+      continue;
+    }
+
+    SectionFragment<E> *frag;
+    i64 addend;
+    std::tie(frag, addend) = get_fragment(ctx, rel);
+
+#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
+#define A (frag ? (u64)addend : (u64)rel.r_addend)
+
+    switch (rel.r_type) {
+    case R_RISCV_32:
+      *(ul32 *)loc = S + A;
+      break;
+    case R_RISCV_64:
+      if (!frag) {
+        if (std::optional<u64> val = get_tombstone(sym)) {
+          *(ul64 *)loc = *val;
+          break;
+        }
+      }
+      *(ul64 *)loc = S + A;
+      break;
+    case R_RISCV_ADD8:
+      *loc += S + A;
+      break;
+    case R_RISCV_ADD16:
+      *(ul16 *)loc += S + A;
+      break;
+    case R_RISCV_ADD32:
+      *(ul32 *)loc += S + A;
+      break;
+    case R_RISCV_ADD64:
+      *(ul64 *)loc += S + A;
+      break;
+    case R_RISCV_SUB8:
+      *loc -= S + A;
+      break;
+    case R_RISCV_SUB16:
+      *(ul16 *)loc -= S + A;
+      break;
+    case R_RISCV_SUB32:
+      *(ul32 *)loc -= S + A;
+      break;
+    case R_RISCV_SUB64:
+      *(ul64 *)loc -= S + A;
+      break;
+    case R_RISCV_SUB6:
+      *loc = (*loc & 0b1100'0000) | ((*loc - (S + A)) & 0b0011'1111);
+      break;
+    case R_RISCV_SET6:
+      *loc = (*loc & 0b1100'0000) | ((S + A) & 0b0011'1111);
+      break;
+    case R_RISCV_SET8:
+      *loc = S + A;
+      break;
+    case R_RISCV_SET16:
+      *(ul16 *)loc = S + A;
+      break;
+    case R_RISCV_SET32:
+      *(ul32 *)loc = S + A;
+      break;
+    default:
+      Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
+                 << rel;
+      break;
+    }
+
+#undef S
+#undef A
+  }
+}
+
+template <typename E>
+void InputSection<E>::copy_contents_riscv(Context<E> &ctx, u8 *buf) {
+  // A non-alloc section isn't relaxed, so just copy it as one big chunk.
+  if (!(shdr().sh_flags & SHF_ALLOC)) {
+    if (compressed)
+      uncompress_to(ctx, buf);
+    else
+      memcpy(buf, contents.data(), contents.size());
+    return;
+  }
+
+  // Memory-allocated sections may be relaxed, so copy each segment
+  // individually.
+  std::span<const ElfRel<E>> rels = get_rels(ctx);
+  i64 pos = 0;
+
+  for (i64 i = 0; i < rels.size(); i++) {
+    i64 delta = extra.r_deltas[i + 1] - extra.r_deltas[i];
+    if (delta == 0)
+      continue;
+    assert(delta < 0);
+
+    const ElfRel<E> &r = rels[i];
+    memcpy(buf, contents.data() + pos, r.r_offset - pos);
+    buf += r.r_offset - pos;
+    pos = r.r_offset - delta;
+  }
+
+  memcpy(buf, contents.data() + pos, contents.size() - pos);
+}
+
+template <typename E>
+void InputSection<E>::scan_relocations(Context<E> &ctx) {
+  assert(shdr().sh_flags & SHF_ALLOC);
+
+  this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
+  std::span<const ElfRel<E>> rels = get_rels(ctx);
+
+  // Scan relocations
+  for (i64 i = 0; i < rels.size(); i++) {
+    const ElfRel<E> &rel = rels[i];
+    if (rel.r_type == R_RISCV_NONE)
+      continue;
+
+    Symbol<E> &sym = *file.symbols[rel.r_sym];
+
+    if (!sym.file) {
+      record_undef_error(ctx, rel);
+      continue;
+    }
+
+    if (sym.get_type() == STT_GNU_IFUNC) {
+      sym.flags |= NEEDS_GOT;
+      sym.flags |= NEEDS_PLT;
+    }
+
+    switch (rel.r_type) {
+    case R_RISCV_32:
+    case R_RISCV_HI20: {
+      Action table[][4] = {
+        // Absolute  Local    Imported data  Imported code
+        {  NONE,     ERROR,   ERROR,         ERROR },      // DSO
+        {  NONE,     ERROR,   ERROR,         ERROR },      // PIE
+        {  NONE,     NONE,    COPYREL,       CPLT  },      // PDE
+      };
+      dispatch(ctx, table, i, rel, sym);
+      break;
+    }
+    case R_RISCV_64: {
+      Action table[][4] = {
+        // Absolute  Local    Imported data  Imported code
+        {  NONE,     BASEREL, DYNREL,        DYNREL },     // DSO
+        {  NONE,     BASEREL, DYNREL,        DYNREL },     // PIE
+        {  NONE,     NONE,    COPYREL,       CPLT   },     // PDE
+      };
+      dispatch(ctx, table, i, rel, sym);
+      break;
+    }
+    case R_RISCV_CALL:
+    case R_RISCV_CALL_PLT:
+      if (sym.is_imported)
+        sym.flags |= NEEDS_PLT;
+      break;
+    case R_RISCV_GOT_HI20:
+      sym.flags |= NEEDS_GOT;
+      break;
+    case R_RISCV_TLS_GOT_HI20:
+      ctx.has_gottp_rel = true;
+      sym.flags |= NEEDS_GOTTP;
+      break;
+    case R_RISCV_TLS_GD_HI20:
+      sym.flags |= NEEDS_TLSGD;
+      break;
+    case R_RISCV_32_PCREL: {
+      Action table[][4] = {
+        // Absolute  Local  Imported data  Imported code
+        {  ERROR,    NONE,  ERROR,         ERROR },      // DSO
+        {  ERROR,    NONE,  COPYREL,       PLT   },      // PIE
+        {  NONE,     NONE,  COPYREL,       PLT   },      // PDE
+      };
+      dispatch(ctx, table, i, rel, sym);
+      break;
+    }
+    case R_RISCV_BRANCH:
+    case R_RISCV_JAL:
+    case R_RISCV_PCREL_HI20:
+    case R_RISCV_PCREL_LO12_I:
+    case R_RISCV_PCREL_LO12_S:
+    case R_RISCV_LO12_I:
+    case R_RISCV_LO12_S:
+    case R_RISCV_TPREL_HI20:
+    case R_RISCV_TPREL_LO12_I:
+    case R_RISCV_TPREL_LO12_S:
+    case R_RISCV_TPREL_ADD:
+    case R_RISCV_ADD8:
+    case R_RISCV_ADD16:
+    case R_RISCV_ADD32:
+    case R_RISCV_ADD64:
+    case R_RISCV_SUB8:
+    case R_RISCV_SUB16:
+    case R_RISCV_SUB32:
+    case R_RISCV_SUB64:
+    case R_RISCV_ALIGN:
+    case R_RISCV_RVC_BRANCH:
+    case R_RISCV_RVC_JUMP:
+    case R_RISCV_RELAX:
+    case R_RISCV_SUB6:
+    case R_RISCV_SET6:
+    case R_RISCV_SET8:
+    case R_RISCV_SET16:
+    case R_RISCV_SET32:
+      break;
+    default:
+      Error(ctx) << *this << ": unknown relocation: " << rel;
+    }
+  }
+}
+
+template <typename E>
+static bool is_resizable(Context<E> &ctx, InputSection<E> *isec) {
+  return isec && (isec->shdr().sh_flags & SHF_ALLOC);
+}
+
+template <typename E>
+static std::vector<Symbol<E> *> get_sorted_symbols(InputSection<E> &isec) {
+  std::vector<Symbol<E> *> vec;
+  for (Symbol<E> *sym : isec.file.symbols)
+    if (sym->file == &isec.file && sym->get_input_section() == &isec)
+      vec.push_back(sym);
+  sort(vec, [](Symbol<E> *a, Symbol<E> *b) { return a->value < b->value; });
+  return vec;
+}
+
+// Returns the distance between a relocated place and a symbol.
+template <typename E>
+static i64 compute_distance(Context<E> &ctx, Symbol<E> &sym,
+                            InputSection<E> &isec, const ElfRel<E> &rel) {
+  // We handle absolute symbols as if they were infinitely far away
+  // because `relax_section` may increase a distance between a branch
+  // instruction and an absolute symbol. Branching to an absolute
+  // location is extremely rare in real code, though.
+  if (sym.is_absolute())
+    return INT32_MAX;
+
+  // Likewise, relocations against weak undefined symbols won't be relaxed.
+  if (sym.esym().is_undef_weak())
+    return INT32_MAX;
+
+  // Compute a distance between the relocated place and the symbol.
+  i64 S = sym.get_addr(ctx);
+  i64 A = rel.r_addend;
+  i64 P = isec.get_addr() + rel.r_offset;
+  return S + A - P;
+}
+
+// Relax R_RISCV_CALL and R_RISCV_CALL_PLT relocations.
+template <typename E>
+static void relax_section(Context<E> &ctx, InputSection<E> &isec) {
+  std::vector<Symbol<E> *> vec = get_sorted_symbols(isec);
+  std::span<Symbol<E> *> syms = vec;
+  i64 delta = 0;
+
+  std::span<const ElfRel<E>> rels = isec.get_rels(ctx);
+  isec.extra.r_deltas.resize(rels.size() + 1);
+
+  for (i64 i = 0; i < rels.size(); i++) {
+    const ElfRel<E> &r = rels[i];
+    i64 delta2 = 0;
+
+    isec.extra.r_deltas[i] = delta;
+
+    switch (r.r_type) {
+    case R_RISCV_ALIGN: {
+      // R_RISCV_ALIGN refers NOP instructions. We need to eliminate
+      // some or all of the instructions so that the instruction that
+      // immediately follows the NOPs is aligned to a specified
+      // alignment boundary.
+      u64 loc = isec.get_addr() + r.r_offset + delta;
+
+      // The total bytes of NOPs is stored to r_addend, so the next
+      // instruction is r_addend away.
+      u64 next_loc = loc + r.r_addend;
+      u64 alignment = bit_ceil(r.r_addend);
+      assert(alignment <= (1 << isec.p2align));
+
+      if (next_loc % alignment)
+        delta2 = align_to(loc, alignment) - next_loc;
+      break;
+    }
+    case R_RISCV_CALL:
+    case R_RISCV_CALL_PLT:
+      if (ctx.arg.relax) {
+        if (i == rels.size() - 1 || rels[i + 1].r_type != R_RISCV_RELAX)
+          break;
+
+        // If the jump target is within ±1 MiB, we can replace AUIPC+JALR
+        // with JAL, saving 4 bytes.
+        Symbol<E> &sym = *isec.file.symbols[r.r_sym];
+        i64 dist = compute_distance(ctx, sym, isec, r);
+        if (dist % 2 == 0 && -(1 << 20) <= dist && dist < (1 << 20))
+          delta2 = -4;
+      }
+    }
+
+    if (delta2 == 0)
+      continue;
+
+    while (!syms.empty() && syms[0]->value <= r.r_offset) {
+      syms[0]->value += delta;
+      syms = syms.subspan(1);
+    }
+
+    delta += delta2;
+  }
+
+  for (Symbol<E> *sym : syms)
+    sym->value += delta;
+  isec.extra.r_deltas[rels.size()] = delta;
+
+  isec.sh_size += delta;
+}
+
+// RISC-V instructions are 16 or 32 bits long, so immediates encoded
+// in instructions can't be 32 bits long. Therefore, branch and load
+// instructions can't refer the 4 GiB address space unlike x86-64.
+// In fact, JAL (jump and link) instruction can jump to only within
+// ±1 MiB as their immediate is only 21 bits long.
+//
+// If you want to jump to somewhere further than that, you need to
+// construct a full 32-bit offset using multiple instruction and
+// branch to that place (e.g. AUIPC and JALR instead of JAL).
+// In this comment, we refer instructions such as JAL as the short
+// encoding and ones such as AUIPC+JALR as the long encoding.
+//
+// By default, compiler always uses the long encoding so that branch
+// targets are always encodable. This is a safe bet for them but
+// may result in inefficient code. Therefore, the RISC-V psABI defines
+// a mechanism for the linker to replace long encoding instructions
+// with short ones, shrinking the section and increasing the code
+// density.
+//
+// This is contrary to the psABIs for the other RISC processors such as
+// ARM64. Typically, they use short instructions by default, and a
+// linker creates so-called "thunks" to extend ranges of short jumps.
+// On RISC-V, instructions are in the long encoding by default, and
+// the linker shrinks them if it can.
+//
+// When we shrink a section, we need to adjust relocation offsets and
+// symbol values. For example, if we replace AUIPC+JALR with JAL
+// (which saves 4 bytes), all relocations pointing to anywhere after
+// that location need to be shifted by 4. In addition to that, any
+// symbol that refers anywhere after that locatioin need to be shifted
+// by 4 bytes as well.
+//
+// For relocations, we use `r_deltas` array to memorize how many bytes
+// have be adjusted. For symbols, we directly mutate their `value`
+// member.
+//
+// This operation seems to be optional, as by default instructions are
+// using the long encoding, but calling this function is actually
+// mandatory because of R_RISCV_ALIGN. R_RISCV_ALIGN relocation is a
+// directive to the linker to align the location referred to by the
+// relocation to a specified byte boundary. We at least have to
+// interpret them satisfy the constraints imposed by R_RISCV_ALIGN
+// relocations.
+template <typename E>
+i64 riscv_resize_sections(Context<E> &ctx) {
+  Timer t(ctx, "riscv_resize_sections");
+
+  // Find R_RISCV_CALL AND R_RISCV_CALL_PLT that can be relaxed.
+  // This step should only shrink sections.
+  tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
+    for (std::unique_ptr<InputSection<E>> &isec : file->sections)
+      if (is_resizable(ctx, isec.get()))
+        relax_section(ctx, *isec);
+  });
+
+  // Re-compute section offset again to finalize them.
+  compute_section_sizes(ctx);
+  return set_osec_offsets(ctx);
+}
+
+#define INSTANTIATE_RISCV(E)                                                 \
+  template void PltSection<E>::copy_buf(Context<E> &);                       \
+  template void PltGotSection<E>::copy_buf(Context<E> &);                    \
+  template void                                                              \
+  EhFrameSection<E>::apply_reloc(Context<E> &, const ElfRel<E> &, u64, u64); \
+  template void InputSection<E>::apply_reloc_alloc(Context<E> &, u8 *);      \
+  template void InputSection<E>::apply_reloc_nonalloc(Context<E> &, u8 *);   \
+  template void InputSection<E>::copy_contents_riscv(Context<E> &, u8 *);    \
+  template void InputSection<E>::scan_relocations(Context<E> &);             \
+  template i64 riscv_resize_sections(Context<E> &);
+
+INSTANTIATE_RISCV(RISCV64);
+INSTANTIATE_RISCV(RISCV32);
+
+} // namespace mold::elf
diff -pruN 1.3.1+dfsg-1/elf/arch-x86-64.cc 1.4.0+dfsg-1/elf/arch-x86-64.cc
--- 1.3.1+dfsg-1/elf/arch-x86-64.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/arch-x86-64.cc	2022-08-04 12:47:21.000000000 +0000
@@ -4,38 +4,22 @@ namespace mold::elf {
 
 using E = X86_64;
 
-// The compact PLT format is used when `-z now` is given. If the flag
-// is given, all PLT symbols are resolved eagerly on startup, so we
-// can omit code for lazy symbol resolution from PLT in that case.
-static void write_compact_plt(Context<E> &ctx) {
-  u8 *buf = ctx.buf + ctx.plt->shdr.sh_offset;
-
-  static const u8 data[] = {
-    0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
-    0x66, 0x90,             // nop
-  };
-
-  for (Symbol<E> *sym : ctx.plt->symbols) {
-    u8 *ent = buf + sym->get_plt_idx(ctx) * ctx.plt_size;
-    memcpy(ent, data, sizeof(data));
-    *(ul32 *)(ent + 2) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 6;
-  }
-}
-
-// The IBTPLT is a security-enhanced version of the regular PLT.
-// It uses Indirect Branch Tracking (IBT) feature which is part of
-// Intel Control-Flow Enforcement (CET).
+// This is a security-enhanced version of the regular PLT. The PLT
+// header and each PLT entry starts with endbr64 for the Intel's
+// control-flow enforcement security mechanism.
 //
-// Note that our IBTPLT instruction sequence is different from the one
-// used in GNU ld. GNU's IBTPLT implementation uses two separate
-// sections (.plt and .plt.sec) in which one PLT entry takes 32 bytes
-// in total. Our PLT consists of just .plt and each entry is 16 bytes
-// long.
+// Note that our IBT-enabled PLT instruction sequence is different
+// from the one used in GNU ld. GNU's IBTPLT implementation uses two
+// separate sections (.plt and .plt.sec) in which one PLT entry takes
+// 32 bytes in total. Our IBTPLT consists of just .plt and each entry
+// is 16 bytes long.
 //
-// Our PLT entry clobbers r11, but that's fine because the resolver
-// function (_dl_runtime_resolve) does not preserve r11 anyway.
-static void write_ibtplt(Context<E> &ctx) {
-  u8 *buf = ctx.buf + ctx.plt->shdr.sh_offset;
+// Our PLT entry clobbers %r11, but that's fine because the resolver
+// function (_dl_runtime_resolve) clobbers %r11 anyway.
+template <>
+void PltSection<E>::copy_buf(Context<E> &ctx) {
+  u8 *buf = ctx.buf + this->shdr.sh_offset;
+  memset(buf, 0xcc, this->shdr.sh_size);
 
   // Write PLT header
   static const u8 plt0[] = {
@@ -43,89 +27,42 @@ static void write_ibtplt(Context<E> &ctx
     0x41, 0x53,             // push %r11
     0xff, 0x35, 0, 0, 0, 0, // push GOTPLT+8(%rip)
     0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip)
-    0x0f, 0x1f, 0x40, 0x00, // nop
-    0x0f, 0x1f, 0x40, 0x00, // nop
-    0x0f, 0x1f, 0x40, 0x00, // nop
-    0x66, 0x90,             // nop
   };
 
   memcpy(buf, plt0, sizeof(plt0));
-  *(ul32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 4;
-  *(ul32 *)(buf + 14) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 2;
+  *(ul32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - this->shdr.sh_addr - 4;
+  *(ul32 *)(buf + 14) = ctx.gotplt->shdr.sh_addr - this->shdr.sh_addr - 2;
 
   // Write PLT entries
-  i64 relplt_idx = 0;
-
   static const u8 data[] = {
     0xf3, 0x0f, 0x1e, 0xfa, // endbr64
     0x41, 0xbb, 0, 0, 0, 0, // mov $index_in_relplt, %r11d
     0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOTPLT
   };
 
-  for (Symbol<E> *sym : ctx.plt->symbols) {
-    u8 *ent = buf + ctx.plt_hdr_size + sym->get_plt_idx(ctx) * ctx.plt_size;
+  for (Symbol<E> *sym : symbols) {
+    i64 idx = sym->get_plt_idx(ctx);
+    u8 *ent = buf + E::plt_hdr_size + idx * E::plt_size;
     memcpy(ent, data, sizeof(data));
-    *(ul32 *)(ent + 6) = relplt_idx++;
+    *(ul32 *)(ent + 6) = idx;
     *(ul32 *)(ent + 12) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 16;
   }
 }
 
-// The regular PLT.
-static void write_plt(Context<E> &ctx) {
-  u8 *buf = ctx.buf + ctx.plt->shdr.sh_offset;
-
-  // Write PLT header
-  static const u8 plt0[] = {
-    0xff, 0x35, 0, 0, 0, 0, // pushq GOTPLT+8(%rip)
-    0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip)
-    0x0f, 0x1f, 0x40, 0x00, // nop
-  };
-
-  memcpy(buf, plt0, sizeof(plt0));
-  *(ul32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 2;
-  *(ul32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 4;
-
-  // Write PLT entries
-  i64 relplt_idx = 0;
-
-  static const u8 data[] = {
-    0xff, 0x25, 0, 0, 0, 0, // jmp   *foo@GOTPLT
-    0x68, 0,    0, 0, 0,    // push  $index_in_relplt
-    0xe9, 0,    0, 0, 0,    // jmp   PLT[0]
-  };
-
-  for (Symbol<E> *sym : ctx.plt->symbols) {
-    u8 *ent = buf + ctx.plt_hdr_size + sym->get_plt_idx(ctx) * ctx.plt_size;
-    memcpy(ent, data, sizeof(data));
-    *(ul32 *)(ent + 2) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 6;
-    *(ul32 *)(ent + 7) = relplt_idx++;
-    *(ul32 *)(ent + 12) = ctx.plt->shdr.sh_addr - sym->get_plt_addr(ctx) - 16;
-  }
-}
-
-template <>
-void PltSection<E>::copy_buf(Context<E> &ctx) {
-  if (ctx.arg.z_now)
-    write_compact_plt(ctx);
-  else if (ctx.arg.z_ibtplt)
-    write_ibtplt(ctx);
-  else
-    write_plt(ctx);
-}
-
 template <>
 void PltGotSection<E>::copy_buf(Context<E> &ctx) {
   u8 *buf = ctx.buf + this->shdr.sh_offset;
+  memset(buf, 0xcc, this->shdr.sh_size);
 
   static const u8 data[] = {
+    0xf3, 0x0f, 0x1e, 0xfa, // endbr64
     0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
-    0x66, 0x90,             // nop
   };
 
   for (Symbol<E> *sym : symbols) {
     u8 *ent = buf + sym->get_pltgot_idx(ctx) * E::pltgot_size;
     memcpy(ent, data, sizeof(data));
-    *(ul32 *)(ent + 2) = sym->get_got_addr(ctx) - sym->get_plt_addr(ctx) - 6;
+    *(ul32 *)(ent + 6) = sym->get_got_addr(ctx) - sym->get_plt_addr(ctx) - 10;
   }
 }
 
diff -pruN 1.3.1+dfsg-1/elf/cmdline.cc 1.4.0+dfsg-1/elf/cmdline.cc
--- 1.3.1+dfsg-1/elf/cmdline.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/cmdline.cc	2022-08-04 12:47:21.000000000 +0000
@@ -1,6 +1,7 @@
 #include "mold.h"
 #include "../cmdline.h"
 
+#include <regex>
 #include <sstream>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -73,11 +74,14 @@ Options:
   --enable-new-dtags          Emit DT_RUNPATH for --rpath (default)
     --disable-new-dtags       Emit DT_RPATH for --rpath
   --dp                        Ignored
-  --dynamic-list              Read a list of dynamic symbols
+  --dynamic-list              Read a list of dynamic symbols (implies -Bsymbolic)
   --eh-frame-hdr              Create .eh_frame_hdr section
     --no-eh-frame-hdr
   --enable-new-dtags          Ignored
   --exclude-libs LIB,LIB,..   Mark all symbols in given libraries hidden
+  --export-dynamic-symbol     Put symbols matching glob in the dynamic symbol table
+  --export-dynamic-symbol-list
+                              Read a list of dynamic symbols
   --fatal-warnings            Treat warnings as errors
     --no-fatal-warnings       Do not treat warnings as errors (default)
   --fini SYMBOL               Call SYMBOL at unload-time
@@ -201,11 +205,13 @@ static std::vector<std::string> add_dash
 
 template <typename E>
 static i64 parse_hex(Context<E> &ctx, std::string opt, std::string_view value) {
-  if (value.starts_with("0x") || value.starts_with("0X"))
-    value = value.substr(2);
-  if (value.find_first_not_of("0123456789abcdefABCDEF") != value.npos)
+  auto flags = std::regex_constants::optimize | std::regex_constants::ECMAScript;
+  static std::regex re(R"((?:0x|0X)?([0-9a-fA-F]+))", flags);
+
+  std::cmatch m;
+  if (!std::regex_match(value.begin(), value.end(), m, re))
     Fatal(ctx) << "option -" << opt << ": not a hexadecimal number";
-  return std::stoul(std::string(value), nullptr, 16);
+  return std::stoul(m[1], nullptr, 16);
 }
 
 template <typename E>
@@ -227,13 +233,11 @@ static i64 parse_number(Context<E> &ctx,
 }
 
 template <typename E>
-static std::vector<u8> parse_hex_build_id(Context<E> &ctx,
-                                          std::string_view arg) {
-  assert(arg.starts_with("0x") || arg.starts_with("0X"));
+static std::vector<u8> parse_hex_build_id(Context<E> &ctx, std::string_view arg) {
+  auto flags = std::regex_constants::optimize | std::regex_constants::ECMAScript;
+  static std::regex re(R"(0[xX]([0-9a-fA-F][0-9a-fA-F])+)", flags);
 
-  if (arg.size() % 2)
-    Fatal(ctx) << "invalid build-id: " << arg;
-  if (arg.substr(2).find_first_not_of("0123456789abcdefABCDEF") != arg.npos)
+  if (!std::regex_match(arg.begin(), arg.end(), re))
     Fatal(ctx) << "invalid build-id: " << arg;
 
   arg = arg.substr(2);
@@ -247,9 +251,9 @@ static std::vector<u8> parse_hex_build_i
     return c - 'A' + 10;
   };
 
-  std::vector<u8> vec(arg.size() / 2);
-  for (i64 i = 0; i < vec.size(); i++)
-    vec[i] = (fn(arg[i * 2]) << 4) | fn(arg[i * 2 + 1]);
+  std::vector<u8> vec;
+  for (i64 i = 0; i < arg.size(); i += 2)
+    vec.push_back((fn(arg[i]) << 4) | fn(arg[i + 1]));
   return vec;
 }
 
@@ -317,28 +321,6 @@ parse_defsym_value(Context<E> &ctx, std:
   return get_symbol(ctx, s);
 }
 
-// Returns a PLT header size and a PLT entry size.
-template <typename E>
-static std::pair<i64, i64> get_plt_size(Context<E> &ctx) {
-  if constexpr (std::is_same_v<E, X86_64>) {
-    if (ctx.arg.z_now)
-      return {0, 8};
-    if (ctx.arg.z_ibtplt)
-      return {32, 16};
-    return {16, 16};
-  }
-
-  if constexpr (std::is_same_v<E, I386>)
-    return {16, 16};
-  if constexpr (std::is_same_v<E, ARM64>)
-    return {32, 16};
-  if constexpr (std::is_same_v<E, ARM32>)
-    return {32, 16};
-  if constexpr (std::is_same_v<E, RISCV64>)
-    return {32, 16};
-  unreachable();
-}
-
 template <typename E>
 std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
   std::span<std::string_view> args = ctx.cmdline_args;
@@ -355,7 +337,7 @@ std::vector<std::string> parse_nonpositi
 
   // RISC-V object files contains lots of local symbols, so by default
   // we discard them. This is compatible with GNU ld.
-  if constexpr (std::is_same_v<E, RISCV64>)
+  if constexpr (std::is_same_v<E, RISCV64> || std::is_same_v<E, RISCV32>)
     ctx.arg.discard_locals = true;
 
   auto read_arg = [&](std::string name) {
@@ -448,19 +430,23 @@ std::vector<std::string> parse_nonpositi
       exit(0);
     } else if (read_flag("V")) {
       SyncOut(ctx) << mold_version
-                   << "\n  Supported emulations:\n   elf_x86_64\n   elf_i386";
+                   << "\n  Supported emulations:\n   elf_x86_64\n   elf_i386\n"
+                   << "   aarch64linux\n   armelf_linux_eabi\n   elf64lriscv\n"
+                   << "   elf32lriscv";
       version_shown = true;
     } else if (read_arg("m")) {
       if (arg == "elf_x86_64") {
-        ctx.arg.emulation = EM_X86_64;
+        ctx.arg.emulation = MachineType::X86_64;
       } else if (arg == "elf_i386") {
-        ctx.arg.emulation = EM_386;
+        ctx.arg.emulation = MachineType::I386;
       } else if (arg == "aarch64linux") {
-        ctx.arg.emulation = EM_AARCH64;
+        ctx.arg.emulation = MachineType::ARM64;
       } else if (arg == "armelf_linux_eabi") {
-        ctx.arg.emulation = EM_ARM;
+        ctx.arg.emulation = MachineType::ARM32;
       } else if (arg == "elf64lriscv") {
-        ctx.arg.emulation = EM_RISCV;
+        ctx.arg.emulation = MachineType::RISCV64;
+      } else if (arg == "elf32lriscv") {
+        ctx.arg.emulation = MachineType::RISCV32;
       } else {
         Fatal(ctx) << "unknown -m argument: " << arg;
       }
@@ -717,9 +703,7 @@ std::vector<std::string> parse_nonpositi
       ctx.arg.z_interpose = true;
     } else if (read_z_flag("ibt")) {
       ctx.arg.z_ibt = true;
-      ctx.arg.z_ibtplt = true;
     } else if (read_z_flag("ibtplt")) {
-      ctx.arg.z_ibtplt = true;
     } else if (read_z_flag("muldefs")) {
       ctx.arg.allow_multiple_definition = true;
     } else if (read_z_flag("keep-text-section-prefix")) {
@@ -907,9 +891,9 @@ std::vector<std::string> parse_nonpositi
     } else if (read_arg("format") || read_arg("b")) {
       if (arg == "binary")
         Fatal(ctx)
-          << "mold does not suppor `-b binary`. If you want to convert a binary"
-          << " file into an object file, use `objcopy -I binary -O default"
-          << " <input-file> <output-file.o>` instead.";
+          << "mold does not support `-b binary`. If you want to convert a"
+          << " binary file into an object file, use `objcopy -I binary -O"
+          << " default <input-file> <output-file.o>` instead.";
       Fatal(ctx) << "unknown command line option: -b " << arg;
     } else if (read_arg("auxiliary") || read_arg("f")) {
       ctx.arg.auxiliary.push_back(arg);
@@ -955,9 +939,21 @@ std::vector<std::string> parse_nonpositi
     } else if (read_z_arg("common-page-size")) {
     } else if (read_flag("no-keep-memory")) {
     } else if (read_arg("version-script")) {
+      // --version-script, --dynamic-list and --export-dynamic-symbol[-list]
+      // are treated as positional arguments even if they are actually not
+      // positional. This is because linker scripts (a positional argument)
+      // can also specify a version script, and it's better to consolidate
+      // parsing in read_input_files. In particular, version scripts can
+      // modify ctx.default_version which we initialize *after* parsing
+      // non-positional args, so the parsing cannot be done right here.
       remaining.push_back("--version-script=" + std::string(arg));
     } else if (read_arg("dynamic-list")) {
+      ctx.arg.Bsymbolic = true;
       remaining.push_back("--dynamic-list=" + std::string(arg));
+    } else if (read_arg("export-dynamic-symbol")) {
+      remaining.push_back("--export-dynamic-symbol=" + std::string(arg));
+    } else if (read_arg("export-dynamic-symbol-list")) {
+      remaining.push_back("--export-dynamic-symbol-list=" + std::string(arg));
     } else if (read_flag("as-needed")) {
       remaining.push_back("--as-needed");
     } else if (read_flag("no-as-needed")) {
@@ -997,7 +993,8 @@ std::vector<std::string> parse_nonpositi
     }
   }
 
-  // Remove redundant `/..` or `/.` from library paths.
+  // Clean library paths by removing redundant `/..` and `/.`
+  // so that they are easier to read in log messages.
   for (std::string &path : ctx.arg.library_paths)
     path = path_clean(path);
 
@@ -1046,8 +1043,6 @@ std::vector<std::string> parse_nonpositi
   if (ctx.arg.shared && warn_shared_textrel)
     ctx.arg.warn_textrel = true;
 
-  std::tie(ctx.plt_hdr_size, ctx.plt_size) = get_plt_size(ctx);
-
   ctx.arg.undefined.push_back(ctx.arg.entry);
 
   // TLSDESC relocs must be always relaxed for statically-linked
diff -pruN 1.3.1+dfsg-1/elf/elf.h 1.4.0+dfsg-1/elf/elf.h
--- 1.3.1+dfsg-1/elf/elf.h	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/elf.h	2022-08-04 12:47:21.000000000 +0000
@@ -23,6 +23,7 @@ struct I386;
 struct ARM64;
 struct ARM32;
 struct RISCV64;
+struct RISCV32;
 
 template <typename E> struct ElfSym;
 template <typename E> struct ElfShdr;
@@ -32,6 +33,8 @@ template <typename E> struct ElfRel;
 template <typename E> struct ElfDyn;
 template <typename E> struct ElfChdr;
 
+enum class MachineType { NONE, X86_64, I386, ARM64, ARM32, RISCV64, RISCV32 };
+
 template <typename E>
 std::string rel_to_string(u32 r_type);
 
@@ -147,6 +150,7 @@ static constexpr u32 ELFCLASS64 = 2;
 
 static constexpr u32 EV_CURRENT = 1;
 
+static constexpr u32 EM_NONE = 0;
 static constexpr u32 EM_386 = 3;
 static constexpr u32 EM_ARM = 40;
 static constexpr u32 EM_X86_64 = 62;
@@ -1040,6 +1044,11 @@ inline std::string rel_to_string<RISCV64
   return "unknown (" + std::to_string(r_type) + ")";
 }
 
+template <>
+inline std::string rel_to_string<RISCV32>(u32 r_type) {
+  return rel_to_string<RISCV64>(r_type);
+}
+
 static constexpr u32 DW_EH_PE_absptr = 0;
 static constexpr u32 DW_EH_PE_omit = 0xff;
 static constexpr u32 DW_EH_PE_uleb128 = 0x01;
@@ -1356,10 +1365,13 @@ struct X86_64 {
   static constexpr u32 R_DTPMOD = R_X86_64_DTPMOD64;
   static constexpr u32 R_TLSDESC = R_X86_64_TLSDESC;
 
+  static constexpr MachineType machine_type = MachineType::X86_64;
   static constexpr u32 word_size = 8;
   static constexpr u32 page_size = 4096;
   static constexpr u32 e_machine = EM_X86_64;
-  static constexpr u32 pltgot_size = 8;
+  static constexpr u32 plt_hdr_size = 32;
+  static constexpr u32 plt_size = 16;
+  static constexpr u32 pltgot_size = 16;
   static constexpr bool is_rel = false;
   static constexpr bool supports_tlsdesc = true;
 };
@@ -1387,9 +1399,12 @@ struct I386 {
   static constexpr u32 R_DTPMOD = R_386_TLS_DTPMOD32;
   static constexpr u32 R_TLSDESC = R_386_TLS_DESC;
 
+  static constexpr MachineType machine_type = MachineType::I386;
   static constexpr u32 word_size = 4;
   static constexpr u32 page_size = 4096;
   static constexpr u32 e_machine = EM_386;
+  static constexpr u32 plt_hdr_size = 16;
+  static constexpr u32 plt_size = 16;
   static constexpr u32 pltgot_size = 8;
   static constexpr bool is_rel = true;
   static constexpr bool supports_tlsdesc = true;
@@ -1418,10 +1433,14 @@ struct ARM64 {
   static constexpr u32 R_DTPMOD = R_AARCH64_TLS_DTPMOD64;
   static constexpr u32 R_TLSDESC = R_AARCH64_TLSDESC;
 
+  static constexpr MachineType machine_type = MachineType::ARM64;
   static constexpr u32 word_size = 8;
   static constexpr u32 page_size = 65536;
   static constexpr u32 e_machine = EM_AARCH64;
+  static constexpr u32 plt_hdr_size = 32;
+  static constexpr u32 plt_size = 16;
   static constexpr u32 pltgot_size = 16;
+  static constexpr u32 tls_offset = 16;
   static constexpr bool is_rel = false;
   static constexpr bool supports_tlsdesc = true;
 };
@@ -1449,10 +1468,14 @@ struct ARM32 {
   static constexpr u32 R_DTPMOD = R_ARM_TLS_DTPMOD32;
   static constexpr u32 R_TLSDESC = R_ARM_TLS_DESC;
 
+  static constexpr MachineType machine_type = MachineType::ARM32;
   static constexpr u32 word_size = 4;
   static constexpr u32 page_size = 4096;
   static constexpr u32 e_machine = EM_ARM;
+  static constexpr u32 plt_hdr_size = 32;
+  static constexpr u32 plt_size = 16;
   static constexpr u32 pltgot_size = 16;
+  static constexpr u32 tls_offset = 8;
   static constexpr bool is_rel = true;
   static constexpr bool supports_tlsdesc = true;
 };
@@ -1479,10 +1502,14 @@ struct RISCV64 {
   static constexpr u32 R_TPOFF = R_RISCV_TLS_TPREL64;
   static constexpr u32 R_DTPMOD = R_RISCV_TLS_DTPMOD64;
 
+  static constexpr MachineType machine_type = MachineType::RISCV64;
   static constexpr u32 word_size = 8;
   static constexpr u32 page_size = 4096;
   static constexpr u32 e_machine = EM_RISCV;
+  static constexpr u32 plt_hdr_size = 32;
+  static constexpr u32 plt_size = 16;
   static constexpr u32 pltgot_size = 16;
+  static constexpr u32 tls_offset = 0;
   static constexpr bool is_rel = false;
   static constexpr bool supports_tlsdesc = false;
 };
@@ -1495,4 +1522,38 @@ template <> struct ElfRel<RISCV64> : pub
 template <> struct ElfDyn<RISCV64> : public Elf64Dyn {};
 template <> struct ElfChdr<RISCV64> : public Elf64Chdr {};
 
+struct RISCV32 {
+  using WordTy = ul32;
+
+  static constexpr u32 R_NONE = R_RISCV_NONE;
+  static constexpr u32 R_COPY = R_RISCV_COPY;
+  static constexpr u32 R_GLOB_DAT = R_RISCV_32;
+  static constexpr u32 R_JUMP_SLOT = R_RISCV_JUMP_SLOT;
+  static constexpr u32 R_ABS = R_RISCV_32;
+  static constexpr u32 R_RELATIVE = R_RISCV_RELATIVE;
+  static constexpr u32 R_IRELATIVE = R_RISCV_IRELATIVE;
+  static constexpr u32 R_DTPOFF = R_RISCV_TLS_DTPREL32;
+  static constexpr u32 R_TPOFF = R_RISCV_TLS_TPREL32;
+  static constexpr u32 R_DTPMOD = R_RISCV_TLS_DTPMOD32;
+
+  static constexpr MachineType machine_type = MachineType::RISCV32;
+  static constexpr u32 word_size = 4;
+  static constexpr u32 page_size = 4096;
+  static constexpr u32 e_machine = EM_RISCV;
+  static constexpr u32 plt_hdr_size = 32;
+  static constexpr u32 plt_size = 16;
+  static constexpr u32 pltgot_size = 16;
+  static constexpr u32 tls_offset = 0;
+  static constexpr bool is_rel = false;
+  static constexpr bool supports_tlsdesc = false;
+};
+
+template <> struct ElfSym<RISCV32> : public Elf32Sym {};
+template <> struct ElfShdr<RISCV32> : public Elf32Shdr {};
+template <> struct ElfEhdr<RISCV32> : public Elf32Ehdr {};
+template <> struct ElfPhdr<RISCV32> : public Elf32Phdr {};
+template <> struct ElfRel<RISCV32> : public Elf32Rela {};
+template <> struct ElfDyn<RISCV32> : public Elf32Dyn {};
+template <> struct ElfChdr<RISCV32> : public Elf32Chdr {};
+
 } // namespace mold::elf
diff -pruN 1.3.1+dfsg-1/elf/gc-sections.cc 1.4.0+dfsg-1/elf/gc-sections.cc
--- 1.3.1+dfsg-1/elf/gc-sections.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/gc-sections.cc	2022-08-04 12:47:21.000000000 +0000
@@ -73,10 +73,9 @@ static void visit(Context<E> &ctx, Input
 }
 
 template <typename E>
-static tbb::concurrent_vector<InputSection<E> *>
-collect_root_set(Context<E> &ctx) {
+static void collect_root_set(Context<E> &ctx,
+                             tbb::concurrent_vector<InputSection<E> *> &rootset) {
   Timer t(ctx, "collect_root_set");
-  tbb::concurrent_vector<InputSection<E> *> rootset;
 
   auto enqueue_section = [&](InputSection<E> *isec) {
     if (mark_section(isec))
@@ -136,8 +135,6 @@ collect_root_set(Context<E> &ctx) {
       for (const ElfRel<E> &rel : cie.get_rels())
         enqueue_symbol(file->symbols[rel.r_sym]);
   });
-
-  return rootset;
 }
 
 // Mark all reachable sections
@@ -191,7 +188,8 @@ void gc_sections(Context<E> &ctx) {
 
   mark_nonalloc_fragments(ctx);
 
-  tbb::concurrent_vector<InputSection<E> *> rootset = collect_root_set(ctx);
+  tbb::concurrent_vector<InputSection<E> *> rootset;
+  collect_root_set(ctx, rootset);
   mark(ctx, rootset);
   sweep(ctx);
 }
diff -pruN 1.3.1+dfsg-1/elf/input-files.cc 1.4.0+dfsg-1/elf/input-files.cc
--- 1.3.1+dfsg-1/elf/input-files.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/input-files.cc	2022-08-04 12:47:21.000000000 +0000
@@ -324,7 +324,7 @@ void ObjectFile<E>::initialize_ehframe_s
 // This function parses an input .eh_frame section.
 template <typename E>
 void ObjectFile<E>::read_ehframe(Context<E> &ctx, InputSection<E> &isec) {
-  std::span<const ElfRel<E>> rels = isec.get_rels(ctx);
+  std::span<ElfRel<E>> rels = isec.get_rels(ctx);
   i64 cies_begin = cies.size();
   i64 fdes_begin = fdes.size();
 
@@ -498,10 +498,9 @@ void ObjectFile<E>::initialize_symbols(C
 // We expect them to be sorted, so sort them if necessary.
 template <typename E>
 void ObjectFile<E>::sort_relocations(Context<E> &ctx) {
-  if constexpr (std::is_same_v<E, RISCV64>) {
+  if constexpr (std::is_same_v<E, RISCV64> || std::is_same_v<E, RISCV32>) {
     auto less = [&](const ElfRel<E> &a, const ElfRel<E> &b) {
-      return a.r_type != E::R_NONE && b.r_type != E::R_NONE &&
-             a.r_offset < b.r_offset;
+      return a.r_offset < b.r_offset;
     };
 
     for (i64 i = 1; i < sections.size(); i++) {
@@ -509,12 +508,9 @@ void ObjectFile<E>::sort_relocations(Con
       if (!isec || !isec->is_alive || !(isec->shdr().sh_flags & SHF_ALLOC))
         continue;
 
-      std::span<const ElfRel<E>> rels = isec->get_rels(ctx);
-      if (std::is_sorted(rels.begin(), rels.end(), less))
-        continue;
-
-      isec->extra.sorted_rels = {rels.begin(), rels.end()};
-      sort(isec->extra.sorted_rels, less);
+      std::span<ElfRel<E>> rels = isec->get_rels(ctx);
+      if (!std::is_sorted(rels.begin(), rels.end(), less))
+        sort(rels, less);
     }
   }
 }
@@ -681,7 +677,7 @@ void ObjectFile<E>::register_section_pie
     if (!isec || !isec->is_alive || !(isec->shdr().sh_flags & SHF_ALLOC))
       continue;
 
-    std::span<const ElfRel<E>> rels = isec->get_rels(ctx);
+    std::span<ElfRel<E>> rels = isec->get_rels(ctx);
     if (rels.empty())
       continue;
 
@@ -755,31 +751,29 @@ void ObjectFile<E>::register_section_pie
 
 template <typename E>
 void ObjectFile<E>::fill_addrsig(Context<E> &ctx) {
+  // Parse a .llvm_addrsig section.
   if (llvm_addrsig) {
-    const u8 *start = reinterpret_cast<const u8 *>(llvm_addrsig->contents.data());
-    const u8 *end = start + llvm_addrsig->contents.size();
-    const u8 *cur = start;
+    u8 *cur = (u8 *)llvm_addrsig->contents.data();
+    u8 *end = cur + llvm_addrsig->contents.size();
+
     while (cur != end) {
-      u64 symIndex = read_uleb(cur);
-      if (this->symbols[symIndex]->file != this) continue;
-      InputSection<E> *section = this->symbols[symIndex]->get_input_section();
-      if (section)
-        section->address_significant = true;
+      Symbol<E> &sym = *this->symbols[read_uleb(cur)];
+      if (sym.file == this)
+        if (InputSection<E> *isec = sym.get_input_section())
+          isec->address_significant = true;
     }
   }
 
-  for (Symbol<E> *sym : this->symbols) {
-    if (sym->file != this) continue;
-    InputSection<E> *section = sym->get_input_section();
-    if (
-        section &&
-            (!llvm_addrsig // We don't have address significance information and needs to be safe
-                || (sym->is_imported || sym->is_exported)) // The symbol might be referenced from the
-                                                           // outside in an address-significant manner
-        ) {
-      section->address_significant = true;
-    }
-  }
+  // We treat a symbol's address as significant if
+  //
+  // 1. we have no address significance information for the symbol, or
+  // 2. the symbol could be referenced from the outside in an address-
+  //    significant manner.
+  for (Symbol<E> *sym : this->symbols)
+    if (sym->file == this)
+      if (InputSection<E> *isec = sym->get_input_section())
+        if (!llvm_addrsig || sym->is_imported || sym->is_exported)
+          isec->address_significant = true;
 }
 
 template <typename E>
@@ -1094,7 +1088,7 @@ void ObjectFile<E>::scan_relocations(Con
 
   // Scan relocations against exception frames
   for (CieRecord<E> &cie : cies) {
-    for (const ElfRel<E> &rel : cie.get_rels()) {
+    for (ElfRel<E> &rel : cie.get_rels()) {
       Symbol<E> &sym = *this->symbols[rel.r_sym];
 
       if (sym.is_imported) {
@@ -1123,9 +1117,9 @@ void ObjectFile<E>::scan_relocations(Con
 // definition, the tentative definition gets the default initial value 0.
 //
 // Tentative definitions are represented as "common symbols" in an object
-// file. In this function, we allocate spaces in .bss for remaining common
-// symbols that were not resolved to usual defined symbols in previous
-// passes.
+// file. In this function, we allocate spaces in .common or .tls_common
+// for remaining common symbols that were not resolved to usual defined
+// symbols in previous passes.
 template <typename E>
 void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
   if (!has_common_symbol)
@@ -1241,56 +1235,49 @@ void ObjectFile<E>::compute_symtab(Conte
     if (sym.file == this && is_alive(sym) &&
         (!ctx.arg.retain_symbols_file || sym.write_to_symtab)) {
       this->strtab_size += sym.name().size() + 1;
-      this->num_global_symtab++;
+      // Global symbols can be demoted to local symbols based on visibility,
+      // version scripts etc.
+      if (sym.is_local())
+        this->num_local_symtab++;
+      else
+        this->num_global_symtab++;
       sym.write_to_symtab = true;
     }
   }
 }
 
 template <typename E>
-void ObjectFile<E>::write_symtab(Context<E> &ctx) {
+void ObjectFile<E>::export_to_symtab(Context<E> &ctx) {
   ElfSym<E> *symtab_base = (ElfSym<E> *)(ctx.buf + ctx.symtab->shdr.sh_offset);
-  i64 symtab_idx;
 
   u8 *strtab_base = ctx.buf + ctx.strtab->shdr.sh_offset;
   i64 strtab_off = this->strtab_offset;
 
-  auto write_sym = [&](Symbol<E> &sym) {
+  auto write_sym = [&](Symbol<E> &sym, i64 &symtab_idx) {
     ElfSym<E> &esym = symtab_base[symtab_idx++];
 
-    esym = sym.esym();
-    esym.st_name = strtab_off;
-
-    if (sym.get_type() == STT_TLS)
-      esym.st_value = sym.get_addr(ctx, false) - ctx.tls_begin;
-    else
-      esym.st_value = sym.get_addr(ctx, false);
-
-    if (InputSection<E> *isec = sym.get_input_section())
-      esym.st_shndx = isec->output_section->shndx;
-    else if (sym.shndx < 0)
-      esym.st_shndx = -sym.shndx;
-    else if (esym.is_undef())
-      esym.st_shndx = SHN_UNDEF;
-    else
-      esym.st_shndx = SHN_ABS;
+    get_output_esym(ctx, sym, strtab_off, esym);
 
     write_string(strtab_base + strtab_off, sym.name());
     strtab_off += sym.name().size() + 1;
   };
 
-  symtab_idx = this->local_symtab_idx;
+  i64 local_symtab_idx = this->local_symtab_idx;
+  i64 global_symtab_idx = this->global_symtab_idx;
   for (i64 i = 1; i < this->first_global; i++) {
     Symbol<E> &sym = *this->symbols[i];
     if (sym.write_to_symtab)
-      write_sym(sym);
+      write_sym(sym, local_symtab_idx);
   }
 
-  symtab_idx = this->global_symtab_idx;
   for (i64 i = this->first_global; i < this->elf_syms.size(); i++) {
     Symbol<E> &sym = *this->symbols[i];
-    if (sym.file == this && sym.write_to_symtab)
-      write_sym(sym);
+    if (sym.file == this && sym.write_to_symtab) {
+      if (sym.is_local())
+        write_sym(sym, local_symtab_idx);
+      else
+        write_sym(sym, global_symtab_idx);
+    }
   }
 }
 
@@ -1506,7 +1493,7 @@ void SharedFile<E>::compute_symtab(Conte
 }
 
 template <typename E>
-void SharedFile<E>::write_symtab(Context<E> &ctx) {
+void SharedFile<E>::export_to_symtab(Context<E> &ctx) {
   ElfSym<E> *symtab =
     (ElfSym<E> *)(ctx.buf + ctx.symtab->shdr.sh_offset) + this->global_symtab_idx;
 
diff -pruN 1.3.1+dfsg-1/elf/input-sections.cc 1.4.0+dfsg-1/elf/input-sections.cc
--- 1.3.1+dfsg-1/elf/input-sections.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/input-sections.cc	2022-08-04 12:47:21.000000000 +0000
@@ -176,12 +176,9 @@ void InputSection<E>::dispatch(Context<E
   case PLT:
     sym.flags |= NEEDS_PLT;
     return;
-  case CPLT: {
-    std::scoped_lock lock(sym.mu);
-    sym.flags |= NEEDS_PLT;
-    sym.is_canonical = true;
+  case CPLT:
+    sym.flags |= NEEDS_CPLT;
     return;
-  }
   case DYNREL:
     if (!is_writable) {
       if (ctx.arg.z_text) {
@@ -219,7 +216,7 @@ void InputSection<E>::write_to(Context<E
     return;
 
   // Copy data
-  if constexpr (std::is_same_v<E, RISCV64>) {
+  if constexpr (std::is_same_v<E, RISCV64> || std::is_same_v<E, RISCV32>) {
     copy_contents_riscv(ctx, buf);
   } else if (compressed) {
     uncompress_to(ctx, buf);
diff -pruN 1.3.1+dfsg-1/elf/linker-script.cc 1.4.0+dfsg-1/elf/linker-script.cc
--- 1.3.1+dfsg-1/elf/linker-script.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/linker-script.cc	2022-08-04 12:47:21.000000000 +0000
@@ -234,7 +234,7 @@ void parse_linker_script(Context<E> &ctx
 }
 
 template <typename E>
-i64 get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
+MachineType get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
   current_file<E> = mf;
 
   std::vector<std::string_view> vec = tokenize(ctx, mf->get_contents());
@@ -242,11 +242,11 @@ i64 get_script_output_type(Context<E> &c
 
   if (tok.size() >= 3 && tok[0] == "OUTPUT_FORMAT" && tok[1] == "(") {
     if (tok[2] == "elf64-x86-64")
-      return EM_X86_64;
+      return MachineType::X86_64;
     if (tok[2] == "elf32-i386")
-      return EM_386;
+      return MachineType::I386;
   }
-  return -1;
+  return MachineType::NONE;
 }
 
 static bool read_label(std::span<std::string_view> &tok,
@@ -395,15 +395,15 @@ void parse_dynamic_list(Context<E> &ctx,
     SyntaxError(ctx, tok[0]) << "trailing garbage token";
 }
 
-#define INSTANTIATE(E)                                                  \
-  template                                                              \
-  void parse_linker_script(Context<E> &ctx, MappedFile<Context<E>> *mf); \
-  template                                                              \
-  i64 get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf); \
-  template                                                              \
-  void parse_version_script(Context<E> &ctx, std::string path);         \
-  template                                                              \
-  void parse_dynamic_list(Context<E> &ctx, std::string path)
+#define INSTANTIATE(E)                                                          \
+  template                                                                      \
+  void parse_linker_script(Context<E> &, MappedFile<Context<E>> *);             \
+  template                                                                      \
+  MachineType get_script_output_type(Context<E> &, MappedFile<Context<E>> *);   \
+  template                                                                      \
+  void parse_version_script(Context<E> &, std::string);                         \
+  template                                                                      \
+  void parse_dynamic_list(Context<E> &, std::string);
 
 INSTANTIATE_ALL;
 
diff -pruN 1.3.1+dfsg-1/elf/lto.cc 1.4.0+dfsg-1/elf/lto.cc
--- 1.3.1+dfsg-1/elf/lto.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/lto.cc	2022-08-04 12:47:21.000000000 +0000
@@ -109,16 +109,33 @@ static std::vector<PluginSymbol> plugin_
 static ClaimFileHandler *claim_file_hook;
 static AllSymbolsReadHandler *all_symbols_read_hook;
 static CleanupHandler *cleanup_hook;
+static bool is_gcc_linker_api_v1 = false;
 
 // Event handlers
 
-static PluginStatus message(int level, const char *fmt, ...) {
+template <typename E>
+static PluginStatus message(PluginLevel level, const char *fmt, ...) {
   LOG << "message\n";
+  Context<E> &ctx = *gctx<E>;
+
+  char buf[1000];
   va_list ap;
   va_start(ap, fmt);
-  fprintf(stderr, "mold: ");
-  vfprintf(stderr, fmt, ap);
-  fprintf(stderr, "\n");
+  vsnprintf(buf, sizeof(buf), fmt, ap);
+  va_end(ap);
+
+  switch (level) {
+  case LDPL_INFO:
+    SyncOut(ctx) << buf;
+    break;
+  case LDPL_WARNING:
+    Warn(ctx) << buf;
+    break;
+  case LDPL_ERROR:
+  case LDPL_FATAL:
+    Fatal(ctx) << buf;
+  }
+
   return LDPS_OK;
 }
 
@@ -399,6 +416,27 @@ get_wrap_symbols(uint64_t *num_symbols,
 }
 
 template <typename E>
+static PluginLinkerAPIVersion
+get_api_version(const char *plugin_identifier,
+                unsigned plugin_version,
+                int minimal_api_supported,
+                int maximal_api_supported,
+                const char **linker_identifier,
+                const char **linker_version) {
+  if (LAPI_V1 < minimal_api_supported)
+    Fatal(*gctx<E>) << "LTO plugin does not support V0 or V1 API";
+
+  *linker_identifier = "mold";
+  *linker_version = MOLD_VERSION;
+
+  if (LAPI_V1 <= maximal_api_supported) {
+    is_gcc_linker_api_v1 = true;
+    return LAPI_V1;
+  }
+  return LAPI_V0;
+}
+
+template <typename E>
 static void load_plugin(Context<E> &ctx) {
   assert(phase == 0);
   phase = 1;
@@ -418,7 +456,7 @@ static void load_plugin(Context<E> &ctx)
   };
 
   std::vector<PluginTagValue> tv;
-  tv.emplace_back(LDPT_MESSAGE, message);
+  tv.emplace_back(LDPT_MESSAGE, message<E>);
 
   if (ctx.arg.shared)
     tv.emplace_back(LDPT_LINKER_OUTPUT, LDPO_DYN);
@@ -449,6 +487,7 @@ static void load_plugin(Context<E> &ctx)
   tv.emplace_back(LDPT_GET_INPUT_SECTION_CONTENTS, get_input_section_contents);
   tv.emplace_back(LDPT_UPDATE_SECTION_ORDER, update_section_order);
   tv.emplace_back(LDPT_ALLOW_SECTION_ORDERING, allow_section_ordering);
+  tv.emplace_back(LDPT_ADD_SYMBOLS_V2, add_symbols);
   tv.emplace_back(LDPT_GET_SYMBOLS_V2, get_symbols_v2<E>);
   tv.emplace_back(LDPT_ALLOW_UNIQUE_SEGMENT_FOR_SECTIONS,
                   allow_unique_segment_for_sections);
@@ -458,9 +497,11 @@ static void load_plugin(Context<E> &ctx)
   tv.emplace_back(LDPT_GET_INPUT_SECTION_SIZE, get_input_section_size);
   tv.emplace_back(LDPT_REGISTER_NEW_INPUT_HOOK, register_new_input_hook<E>);
   tv.emplace_back(LDPT_GET_WRAP_SYMBOLS, get_wrap_symbols);
+  tv.emplace_back(LDPT_GET_API_VERSION, get_api_version<E>);
   tv.emplace_back(LDPT_NULL, 0);
 
-  onload(tv.data());
+  PluginStatus status = onload(tv.data());
+  assert(status == LDPS_OK);
 }
 
 template <typename E>
@@ -524,15 +565,19 @@ static bool is_llvm(Context<E> &ctx) {
 }
 
 // Returns true if a given linker plugin supports the get_symbols_v3 API.
-// Currently, we simply assume that LLVM supports it and GCC does not.
+// Any version of LLVM and GCC 12 or newer support it.
 template <typename E>
 static bool suppots_v3_api(Context<E> &ctx) {
-  return is_llvm(ctx);
+  return is_gcc_linker_api_v1 || is_llvm(ctx);
 }
 
 template <typename E>
 ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
-  LOG << "read_lto_object: " << mf->name << "\n";
+  // V0 API's claim_file is not thread-safe.
+  static std::mutex mu;
+  std::unique_lock lock(mu, std::defer_lock);
+  if (!is_gcc_linker_api_v1)
+    lock.lock();
 
   if (ctx.arg.plugin.empty())
     Fatal(ctx) << mf->name << ": don't know how to handle this LTO object file "
@@ -620,7 +665,6 @@ std::vector<ObjectFile<E> *> do_lto(Cont
       return;
 
     for (i64 i = file->first_global; i < file->symbols.size(); i++) {
-      ElfSym<E> &esym = file->elf_syms[i];
       Symbol<E> &sym = *file->symbols[i];
 
       if (sym.file && !sym.file->is_dso &&
diff -pruN 1.3.1+dfsg-1/elf/lto.h 1.4.0+dfsg-1/elf/lto.h
--- 1.3.1+dfsg-1/elf/lto.h	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/lto.h	2022-08-04 12:47:21.000000000 +0000
@@ -44,6 +44,7 @@ enum PluginTag {
   LDPT_REGISTER_NEW_INPUT_HOOK,
   LDPT_GET_WRAP_SYMBOLS,
   LDPT_ADD_SYMBOLS_V2,
+  LDPT_GET_API_VERSION,
 };
 
 enum PluginApiVersion {
@@ -141,6 +142,11 @@ enum PluginLevel {
   LDPL_FATAL,
 };
 
+enum PluginLinkerAPIVersion {
+  LAPI_V0 = 0,
+  LAPI_V1,
+};
+
 typedef PluginStatus OnloadFn(PluginTagValue *tv);
 typedef PluginStatus ClaimFileHandler(const PluginInputFile *, int *);
 typedef PluginStatus AllSymbolsReadHandler();
diff -pruN 1.3.1+dfsg-1/elf/main.cc 1.4.0+dfsg-1/elf/main.cc
--- 1.3.1+dfsg-1/elf/main.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/main.cc	2022-08-04 12:47:21.000000000 +0000
@@ -17,11 +17,55 @@
 
 namespace mold::elf {
 
+// Read the beginning of a given file and returns its machine type
+// (e.g. EM_X86_64 or EM_386).
+template <typename E>
+static MachineType get_machine_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
+  auto get_elf_type = [](u8 *buf) {
+    switch (ElfEhdr<E> &ehdr = *(ElfEhdr<E> *)buf; ehdr.e_machine) {
+    case EM_386:
+      return MachineType::I386;
+    case EM_X86_64:
+      return MachineType::X86_64;
+    case EM_ARM:
+      return MachineType::ARM32;
+    case EM_AARCH64:
+      return MachineType::ARM64;
+    case EM_RISCV:
+      return (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+        ? MachineType::RISCV64 : MachineType::RISCV32;
+    default:
+      return MachineType::NONE;
+    }
+  };
+
+  switch (get_file_type(mf)) {
+  case FileType::ELF_OBJ:
+  case FileType::ELF_DSO:
+  case FileType::GCC_LTO_OBJ:
+    return get_elf_type(mf->data);
+  case FileType::AR:
+    for (MappedFile<Context<E>> *child : read_fat_archive_members(ctx, mf))
+      if (get_file_type(child) == FileType::ELF_OBJ)
+        return get_elf_type(child->data);
+    return MachineType::NONE;
+  case FileType::THIN_AR:
+    for (MappedFile<Context<E>> *child : read_thin_archive_members(ctx, mf))
+      if (get_file_type(child) == FileType::ELF_OBJ)
+        return get_elf_type(child->data);
+    return MachineType::NONE;
+  case FileType::TEXT:
+    return get_script_output_type(ctx, mf);
+  default:
+    return MachineType::NONE;
+  }
+}
+
 template <typename E>
 static ObjectFile<E> *new_object_file(Context<E> &ctx, MappedFile<Context<E>> *mf,
                                       std::string archive_name) {
-  if (i64 type = ((ElfEhdr<E> *)mf->data)->e_machine; type != E::e_machine)
-    Fatal(ctx) << mf->name << ": incompatible file type: " << type;
+  if (get_machine_type(ctx, mf) != ctx.arg.emulation)
+    Fatal(ctx) << mf->name << ": incompatible file type";
 
   static Counter count("parsed_objs");
   count++;
@@ -58,8 +102,8 @@ static ObjectFile<E> *new_lto_obj(Contex
 template <typename E>
 static SharedFile<E> *
 new_shared_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
-  if (i64 type = ((ElfEhdr<E> *)mf->data)->e_machine; type != E::e_machine)
-    Fatal(ctx) << mf->name << ": incompatible file type: " << type;
+  if (get_machine_type(ctx, mf) != ctx.arg.emulation)
+    Fatal(ctx) << mf->name << ": incompatible file type";
 
   SharedFile<E> *file = SharedFile<E>::create(ctx, mf);
   file->priority = ctx.file_priority++;
@@ -110,44 +154,18 @@ void read_file(Context<E> &ctx, MappedFi
       ctx.objs.push_back(file);
     return;
   default:
-    Fatal(ctx) << mf->name << ": unknown file type: " << type;
+    Fatal(ctx) << mf->name << ": unknown file type";
   }
 }
 
-// Read the beginning of a given file and returns its machine type
-// (e.g. EM_X86_64 or EM_386). Return -1 if unknown.
 template <typename E>
-static i64 get_machine_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
-  switch (get_file_type(mf)) {
-  case FileType::ELF_OBJ:
-  case FileType::ELF_DSO:
-  case FileType::GCC_LTO_OBJ:
-    return ((ElfEhdr<E> *)mf->data)->e_machine;
-  case FileType::AR:
-    for (MappedFile<Context<E>> *child : read_fat_archive_members(ctx, mf))
-      if (get_file_type(child) == FileType::ELF_OBJ)
-        return ((ElfEhdr<E> *)child->data)->e_machine;
-    return -1;
-  case FileType::THIN_AR:
-    for (MappedFile<Context<E>> *child : read_thin_archive_members(ctx, mf))
-      if (get_file_type(child) == FileType::ELF_OBJ)
-        return ((ElfEhdr<E> *)child->data)->e_machine;
-    return -1;
-  case FileType::TEXT:
-    return get_script_output_type(ctx, mf);
-  default:
-    return -1;
-  }
-}
-
-template <typename E>
-static i64
+static MachineType
 deduce_machine_type(Context<E> &ctx, std::span<std::string> args) {
   for (std::string_view arg : args)
     if (!arg.starts_with('-'))
       if (auto *mf = MappedFile<Context<E>>::open(ctx, std::string(arg)))
-        if (i64 type = get_machine_type(ctx, mf); type != -1)
-          return type;
+        if (MachineType ty = get_machine_type(ctx, mf); ty != MachineType::NONE)
+          return ty;
   Fatal(ctx) << "-m option is missing";
 }
 
@@ -157,10 +175,10 @@ MappedFile<Context<E>> *open_library(Con
   if (!mf)
     return nullptr;
 
-  i64 type = get_machine_type(ctx, mf);
-  if (type == -1 || type == E::e_machine)
+  MachineType ty = get_machine_type(ctx, mf);
+  if (ty == MachineType::NONE || ty == E::machine_type)
     return mf;
-  Warn(ctx) << path << ": skipping incompatible file " << (int)type
+  Warn(ctx) << path << ": skipping incompatible file " << (int)ty
             << " " << (int)E::e_machine;
   return nullptr;
 }
@@ -195,7 +213,7 @@ static void read_input_files(Context<E>
   ctx.is_static = ctx.arg.is_static;
 
   while (!args.empty()) {
-    const std::string &arg = args[0];
+    std::string_view arg = args[0];
     args = args.subspan(1);
 
     if (arg == "--as-needed") {
@@ -214,10 +232,17 @@ static void read_input_files(Context<E>
       ctx.in_lib = true;
     } else if (arg == "--end-lib") {
       ctx.in_lib = false;
-    } else if (arg.starts_with("--version-script=")) {
-      parse_version_script(ctx, arg.substr(strlen("--version-script=")));
-    } else if (arg.starts_with("--dynamic-list=")) {
-      parse_dynamic_list(ctx, arg.substr(strlen("--dynamic-list=")));
+    } else if (remove_prefix(arg, "--version-script=")) {
+      parse_version_script(ctx, std::string(arg));
+    } else if (remove_prefix(arg, "--dynamic-list=")) {
+      parse_dynamic_list(ctx, std::string(arg));
+    } else if (remove_prefix(arg, "--export-dynamic-symbol=")) {
+      if (arg == "*")
+        ctx.default_version = VER_NDX_GLOBAL;
+      else
+        ctx.version_patterns.push_back({arg, VER_NDX_GLOBAL, false});
+    } else if (remove_prefix(arg, "--export-dynamic-symbol-list=")) {
+      parse_dynamic_list(ctx, std::string(arg));
     } else if (arg == "--push-state") {
       state.push_back({ctx.as_needed, ctx.whole_archive, ctx.is_static,
                        ctx.in_lib});
@@ -227,8 +252,8 @@ static void read_input_files(Context<E>
       std::tie(ctx.as_needed, ctx.whole_archive, ctx.is_static, ctx.in_lib) =
         state.back();
       state.pop_back();
-    } else if (arg.starts_with("-l")) {
-      MappedFile<Context<E>> *mf = find_library(ctx, arg.substr(2));
+    } else if (remove_prefix(arg, "-l")) {
+      MappedFile<Context<E>> *mf = find_library(ctx, std::string(arg));
       mf->given_fullpath = false;
       read_file(ctx, mf);
     } else {
@@ -373,24 +398,28 @@ static int elf_main(int argc, char **arg
   std::vector<std::string> file_args = parse_nonpositional_args(ctx);
 
   // If no -m option is given, deduce it from input files.
-  if (ctx.arg.emulation == -1)
+  if (ctx.arg.emulation == MachineType::NONE)
     ctx.arg.emulation = deduce_machine_type(ctx, file_args);
 
   // Redo if -m is not x86-64.
-  if (ctx.arg.emulation != E::e_machine) {
+  if (ctx.arg.emulation != E::machine_type) {
 #if !MOLD_DEBUG_X86_64_ONLY && !MOLD_DEBUG_ARM64_ONLY
     switch (ctx.arg.emulation) {
-    case EM_386:
+    case MachineType::I386:
       return elf_main<I386>(argc, argv);
-    case EM_AARCH64:
+    case MachineType::ARM64:
       return elf_main<ARM64>(argc, argv);
-    case EM_ARM:
+    case MachineType::ARM32:
       return elf_main<ARM32>(argc, argv);
-    case EM_RISCV:
+    case MachineType::RISCV64:
       return elf_main<RISCV64>(argc, argv);
+    case MachineType::RISCV32:
+      return elf_main<RISCV32>(argc, argv);
+    default:
+      unreachable();
     }
 #endif
-    Fatal(ctx) << "unknown emulation: " << ctx.arg.emulation;
+    unreachable();
   }
 
   Timer t_all(ctx, "all");
@@ -642,7 +671,7 @@ static int elf_main(int argc, char **arg
   // that they can jump to anywhere in ±2 GiB by default. They may
   // be replaced with shorter instruction sequences if destinations
   // are close enough. Do this optimization.
-  if constexpr (std::is_same_v<E, RISCV64>)
+  if constexpr (std::is_same_v<E, RISCV64> || std::is_same_v<E, RISCV32>)
     filesize = riscv_resize_sections(ctx);
 
   // Fix linker-synthesized symbol addresses.
diff -pruN 1.3.1+dfsg-1/elf/mold.h 1.4.0+dfsg-1/elf/mold.h
--- 1.3.1+dfsg-1/elf/mold.h	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/mold.h	2022-08-04 12:47:21.000000000 +0000
@@ -45,7 +45,8 @@
   INSTANTIATE(I386);                            \
   INSTANTIATE(ARM64);                           \
   INSTANTIATE(ARM32);                           \
-  INSTANTIATE(RISCV64)
+  INSTANTIATE(RISCV64);                         \
+  INSTANTIATE(RISCV32)
 #endif
 
 namespace mold::elf {
@@ -149,7 +150,7 @@ struct SymbolAux {
 template <typename E>
 struct CieRecord {
   CieRecord(Context<E> &ctx, ObjectFile<E> &file, InputSection<E> &isec,
-            u32 input_offset, std::span<const ElfRel<E>> rels, u32 rel_idx)
+            u32 input_offset, std::span<ElfRel<E>> rels, u32 rel_idx)
     : file(file), input_section(isec), input_offset(input_offset),
       rel_idx(rel_idx), rels(rels), contents(file.get_string(ctx, isec.shdr())) {}
 
@@ -161,7 +162,7 @@ struct CieRecord {
     return contents.substr(input_offset, size());
   }
 
-  std::span<const ElfRel<E>> get_rels() const {
+  std::span<ElfRel<E>> get_rels() const {
     i64 end = rel_idx;
     while (end < rels.size() && rels[end].r_offset < input_offset + size())
       end++;
@@ -177,7 +178,7 @@ struct CieRecord {
   u32 rel_idx = -1;
   u32 icf_idx = -1;
   bool is_leader = false;
-  std::span<const ElfRel<E>> rels;
+  std::span<ElfRel<E>> rels;
   std::string_view contents;
 };
 
@@ -202,7 +203,7 @@ struct FdeRecord {
 
   i64 size(ObjectFile<E> &file) const;
   std::string_view get_contents(ObjectFile<E> &file) const;
-  std::span<const ElfRel<E>> get_rels(ObjectFile<E> &file) const;
+  std::span<ElfRel<E>> get_rels(ObjectFile<E> &file) const;
 
   u32 input_offset = -1;
   u32 output_offset = -1;
@@ -250,8 +251,11 @@ struct InputSectionExtras<ARM64> {
 template <>
 struct InputSectionExtras<RISCV64> {
   std::vector<i32> r_deltas;
-  std::vector<Symbol<RISCV64> *> sorted_symbols;
-  std::vector<ElfRel<RISCV64>> sorted_rels;
+};
+
+template <>
+struct InputSectionExtras<RISCV32> {
+  std::vector<i32> r_deltas;
 };
 
 // InputSection represents a section in an input object file.
@@ -274,7 +278,7 @@ public:
   u64 get_addr() const;
   i64 get_addend(const ElfRel<E> &rel) const;
   const ElfShdr<E> &shdr() const;
-  std::span<const ElfRel<E>> get_rels(Context<E> &ctx) const;
+  std::span<ElfRel<E>> get_rels(Context<E> &ctx) const;
   std::span<FdeRecord<E>> get_fdes() const;
   std::string_view get_func_name(Context<E> &ctx, i64 offset);
 
@@ -643,6 +647,10 @@ public:
   void copy_buf(Context<E> &ctx) override;
 };
 
+template<typename E>
+void get_output_esym(Context<E> &ctx, const Symbol<E> &sym, i64 strtab_offset,
+                     ElfSym<E> &out_esym);
+
 template <typename E>
 class SymtabSection : public Chunk<E> {
 public:
@@ -1114,7 +1122,7 @@ public:
   void scan_relocations(Context<E> &ctx);
   void convert_common_symbols(Context<E> &ctx);
   void compute_symtab(Context<E> &ctx);
-  void write_symtab(Context<E> &ctx);
+  void export_to_symtab(Context<E> &ctx);
 
   i64 get_shndx(const ElfSym<E> &esym);
   InputSection<E> *get_section(const ElfSym<E> &esym);
@@ -1196,7 +1204,7 @@ public:
                          std::function<void(InputFile<E> *)> feeder) override;
 
   void compute_symtab(Context<E> &ctx);
-  void write_symtab(Context<E> &ctx);
+  void export_to_symtab(Context<E> &ctx);
 
   bool is_needed = false;
   std::string soname;
@@ -1222,7 +1230,7 @@ template <typename E>
 void parse_linker_script(Context<E> &ctx, MappedFile<Context<E>> *mf);
 
 template <typename E>
-i64 get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf);
+MachineType get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf);
 
 template <typename E>
 void parse_version_script(Context<E> &ctx, std::string path);
@@ -1377,7 +1385,8 @@ void create_range_extension_thunks(Conte
 // arch-riscv64.cc
 //
 
-i64 riscv_resize_sections(Context<RISCV64> &ctx);
+template <typename E>
+i64 riscv_resize_sections(Context<E> &ctx);
 
 //
 // main.cc
@@ -1423,31 +1432,6 @@ struct VersionPattern {
   bool is_cpp = false;
 };
 
-template <typename E, typename T>
-class FileCache {
-public:
-  void store(MappedFile<Context<E>> *mf, T *obj) {
-    Key k(mf->name, mf->size, mf->mtime);
-    cache[k].push_back(obj);
-  }
-
-  std::vector<T *> get(MappedFile<Context<E>> *mf) {
-    Key k(mf->name, mf->size, mf->mtime);
-    std::vector<T *> objs = cache[k];
-    cache[k].clear();
-    return objs;
-  }
-
-  T *get_one(MappedFile<Context<E>> *mf) {
-    std::vector<T *> objs = get(mf);
-    return objs.empty() ? nullptr : objs[0];
-  }
-
-private:
-  typedef std::tuple<std::string, i64, i64> Key;
-  std::map<Key, std::vector<T *>> cache;
-};
-
 // Context represents a context object for each invocation of the linker.
 // It contains command line flags, pointers to singleton objects
 // (such as linker-synthesized output sections), unique_ptrs for
@@ -1526,7 +1510,6 @@ struct Context {
     bool z_execstack = false;
     bool z_execstack_if_needed = false;
     bool z_ibt = false;
-    bool z_ibtplt = false;
     bool z_initfirst = false;
     bool z_interpose = false;
     bool z_keep_text_section_prefix = false;
@@ -1536,7 +1519,7 @@ struct Context {
     bool z_relro = true;
     bool z_shstk = false;
     bool z_text = false;
-    i64 emulation = -1;
+    MachineType emulation = MachineType::NONE;
     i64 filler = -1;
     i64 print_dependencies = 0;
     i64 spare_dynamic_tags = 5;
@@ -1577,8 +1560,6 @@ struct Context {
   std::vector<VersionPattern> version_patterns;
   u16 default_version = VER_NDX_GLOBAL;
   i64 page_size = -1;
-  i64 plt_hdr_size = -1;
-  i64 plt_size = -1;
 
   // Reader context
   bool as_needed = false;
@@ -1598,8 +1579,6 @@ struct Context {
   tbb::concurrent_vector<std::unique_ptr<MergedSection<E>>> merged_sections;
   tbb::concurrent_vector<std::unique_ptr<Chunk<E>>> output_chunks;
   std::vector<std::unique_ptr<OutputSection<E>>> output_sections;
-  FileCache<E, ObjectFile<E>> obj_cache;
-  FileCache<E, SharedFile<E>> dso_cache;
 
   tbb::concurrent_vector<std::unique_ptr<TimerRecord>> timer_records;
   tbb::concurrent_vector<std::function<void()>> on_exit;
@@ -1692,6 +1671,7 @@ struct Context {
   Symbol<E> *_TLS_MODULE_BASE_ = nullptr;
   Symbol<E> *__GNU_EH_FRAME_HDR = nullptr;
   Symbol<E> *__bss_start = nullptr;
+  Symbol<E> *__dso_handle = nullptr;
   Symbol<E> *__ehdr_start = nullptr;
   Symbol<E> *__executable_start = nullptr;
   Symbol<E> *__exidx_end = nullptr;
@@ -1734,12 +1714,16 @@ std::ostream &operator<<(std::ostream &o
 enum {
   NEEDS_GOT                = 1 << 0,
   NEEDS_PLT                = 1 << 1,
-  NEEDS_GOTTP              = 1 << 2,
-  NEEDS_TLSGD              = 1 << 3,
-  NEEDS_COPYREL            = 1 << 4,
-  NEEDS_TLSDESC            = 1 << 5,
-  NEEDS_THUMB_TO_ARM_THUNK = 1 << 6,
-  NEEDS_RANGE_EXTN_THUNK   = 1 << 7,
+  NEEDS_CPLT               = 1 << 2,
+  NEEDS_GOTTP              = 1 << 3,
+  NEEDS_TLSGD              = 1 << 4,
+  NEEDS_COPYREL            = 1 << 5,
+  NEEDS_TLSDESC            = 1 << 6,
+  NEEDS_THUMB_TO_ARM_THUNK = 1 << 7,
+
+  // This is used only by ARM64. Since the flag is used in a different
+  // phase than the above flags, we can reuse the same value.
+  NEEDS_RANGE_EXTN_THUNK   = 1 << 0,
 };
 
 // A struct to hold taret-dependent symbol members.
@@ -1801,6 +1785,7 @@ public:
 
   bool is_absolute() const;
   bool is_relative() const;
+  bool is_local() const;
 
   InputSection<E> *get_input_section() const;
   u32 get_type() const;
@@ -1993,9 +1978,9 @@ inline std::string_view FdeRecord<E>::ge
 }
 
 template <typename E>
-inline std::span<const ElfRel<E>>
+inline std::span<ElfRel<E>>
 FdeRecord<E>::get_rels(ObjectFile<E> &file) const {
-  std::span<const ElfRel<E>> rels = file.cies[cie_idx].rels;
+  std::span<ElfRel<E>> rels = file.cies[cie_idx].rels;
   i64 end = rel_idx;
   while (end < rels.size() && rels[end].r_offset < input_offset + size(file))
     end++;
@@ -2149,12 +2134,9 @@ inline const ElfShdr<E> &InputSection<E>
 }
 
 template <typename E>
-inline std::span<const ElfRel<E>> InputSection<E>::get_rels(Context<E> &ctx) const {
+inline std::span<ElfRel<E>> InputSection<E>::get_rels(Context<E> &ctx) const {
   if (relsec_idx == -1)
     return {};
-  if constexpr (std::is_same_v<E, RISCV64>)
-    if (!extra.sorted_rels.empty())
-      return extra.sorted_rels;
   return file.template get_data<ElfRel<E>>(ctx, file.elf_sections[relsec_idx]);
 }
 
@@ -2394,7 +2376,7 @@ inline u64 Symbol<E>::get_tlsdesc_addr(C
 template <typename E>
 inline u64 Symbol<E>::get_plt_addr(Context<E> &ctx) const {
   if (i32 idx = get_plt_idx(ctx); idx != -1)
-    return ctx.plt->shdr.sh_addr + ctx.plt_hdr_size + idx * ctx.plt_size;
+    return ctx.plt->shdr.sh_addr + E::plt_hdr_size + idx * E::plt_size;
   return ctx.pltgot->shdr.sh_addr + get_pltgot_idx(ctx) * E::pltgot_size;
 }
 
@@ -2516,6 +2498,11 @@ inline bool Symbol<E>::is_relative() con
   return !is_absolute();
 }
 
+template<typename E>
+inline bool Symbol<E>::is_local() const {
+  return !is_imported && !is_exported;
+}
+
 template <typename E>
 inline InputSection<E> *Symbol<E>::get_input_section() const {
   if (shndx > 0) {
diff -pruN 1.3.1+dfsg-1/elf/output-chunks.cc 1.4.0+dfsg-1/elf/output-chunks.cc
--- 1.3.1+dfsg-1/elf/output-chunks.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/output-chunks.cc	2022-08-04 12:47:21.000000000 +0000
@@ -49,16 +49,16 @@ u64 get_eflags(Context<E> &ctx) {
   if constexpr (std::is_same_v<E, ARM32>)
     return EF_ARM_EABI_VER5;
 
-  if constexpr (std::is_same_v<E, RISCV64>) {
-    std::vector<ObjectFile<RISCV64> *> objs = ctx.objs;
+  if constexpr (std::is_same_v<E, RISCV64> || std::is_same_v<E, RISCV32>) {
+    std::vector<ObjectFile<E> *> objs = ctx.objs;
     std::erase(objs, ctx.internal_obj);
 
     if (objs.empty())
       return 0;
 
     u32 ret = objs[0]->get_ehdr().e_flags;
-    for (ObjectFile<RISCV64> *file : std::span(objs).subspan(1))
-      if (file->get_ehdr().e_flags & EF_RISCV_RVC)
+    for (i64 i = 1; i < objs.size(); i++)
+      if (objs[i]->get_ehdr().e_flags & EF_RISCV_RVC)
         ret |= EF_RISCV_RVC;
     return ret;
   }
@@ -356,8 +356,10 @@ void RelDynSection<E>::update_shdr(Conte
 
 template <typename E>
 static ElfRel<E> reloc(u64 offset, u32 type, u32 sym, i64 addend = 0) {
-  if constexpr (std::is_same_v<E, I386> || std::is_same_v<E, ARM32>)
+  if constexpr (E::is_rel)
     return {(u32)offset, (u8)type, sym};
+  else if constexpr (E::word_size == 4)
+    return {offset, (u8)type, sym, addend};
   else
     return {offset, type, sym, addend};
 }
@@ -570,11 +572,11 @@ void SymtabSection<E>::copy_buf(Context<
 
   // Copy symbols and symbol names from input files
   tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
-    file->write_symtab(ctx);
+    file->export_to_symtab(ctx);
   });
 
   tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
-    file->write_symtab(ctx);
+    file->export_to_symtab(ctx);
   });
 }
 
@@ -904,6 +906,7 @@ static std::vector<T> encode_relr(const
 
   for (i64 i = 0; i < pos.size();) {
     assert(i == 0 || pos[i - 1] <= pos[i]);
+    assert(pos[i] % sizeof(T) == 0);
 
     vec.push_back(pos[i]);
     u64 base = pos[i] + sizeof(T);
@@ -1076,17 +1079,18 @@ std::vector<GotEntry<E>> GotSection<E>::
       continue;
     }
 
-    // Otherwise, we know the offset at link-time, so fill the GOT entry.
+    // Otherwise, we know the offset from the thread pointer (TP) at
+    // link-time, so we can fill the GOT entry directly.
+    //
+    // On x86, TP (%gs for 32-bit, %fs for 64-bit) points to the end of
+    // all thread-local variables for a historical reason, so the offset
+    // we calculate here will be negative. On other architectures, TP
+    // points to an optional padding whose size is architecture-dependent
+    // followed by thread-local variables.
     if constexpr (std::is_same_v<E, X86_64> || std::is_same_v<E, I386>)
       entries.push_back({idx, sym->get_addr(ctx) - ctx.tls_end});
-    else if constexpr (std::is_same_v<E, ARM32>)
-      entries.push_back({idx, sym->get_addr(ctx) - ctx.tls_begin + 8});
-    else if constexpr (std::is_same_v<E, ARM64>)
-      entries.push_back({idx, sym->get_addr(ctx) - ctx.tls_begin + 16});
-    else if constexpr (std::is_same_v<E, RISCV64>)
-      entries.push_back({idx, sym->get_addr(ctx) - ctx.tls_begin});
     else
-      unreachable();
+      entries.push_back({idx, sym->get_addr(ctx) - ctx.tls_begin + E::tls_offset});
   }
 
   if (tlsld_idx != -1)
@@ -1136,24 +1140,12 @@ void GotPltSection<E>::copy_buf(Context<
   buf[1] = 0;
   buf[2] = 0;
 
-  auto get_plt_resolver_addr = [&](Symbol<E> &sym) -> u64 {
-    if constexpr (std::is_same_v<E, ARM64> || std::is_same_v<E, ARM32> ||
-                  std::is_same_v<E, RISCV64>)
-      return ctx.plt->shdr.sh_addr;
-
-    if constexpr (std::is_same_v<E, X86_64>) {
-      if (ctx.arg.z_ibtplt)
-        return ctx.plt->shdr.sh_addr;
-      return sym.get_plt_addr(ctx) + 6;
-    }
-
+  for (Symbol<E> *sym : ctx.plt->symbols) {
     if constexpr (std::is_same_v<E, I386>)
-      return sym.get_plt_addr(ctx) + 6;
-    unreachable();
-  };
-
-  for (Symbol<E> *sym : ctx.plt->symbols)
-    buf[sym->get_gotplt_idx(ctx)] = get_plt_resolver_addr(*sym);
+      buf[sym->get_gotplt_idx(ctx)] = sym->get_plt_addr(ctx) + 6;
+    else
+      buf[sym->get_gotplt_idx(ctx)] = ctx.plt->shdr.sh_addr;
+  }
 }
 
 template <typename E>
@@ -1161,10 +1153,10 @@ void PltSection<E>::add_symbol(Context<E
   assert(!sym->has_plt(ctx));
 
   if (this->shdr.sh_size == 0)
-    this->shdr.sh_size = ctx.plt_hdr_size;
+    this->shdr.sh_size = E::plt_hdr_size;
 
   sym->set_plt_idx(ctx, symbols.size());
-  this->shdr.sh_size += ctx.plt_size;
+  this->shdr.sh_size += E::plt_size;
   symbols.push_back(sym);
 
   sym->set_gotplt_idx(ctx, ctx.gotplt->shdr.sh_size / E::word_size);
@@ -1199,6 +1191,52 @@ void RelPltSection<E>::copy_buf(Context<
                                  sym->get_dynsym_idx(ctx));
 }
 
+template<typename E>
+void get_output_esym(Context<E> &ctx, const Symbol<E> &sym, i64 strtab_offset,
+                     ElfSym<E> &out_esym) {
+  memset(&out_esym, 0, sizeof(out_esym));
+  out_esym.st_type = sym.esym().st_type;
+  out_esym.st_size = sym.esym().st_size;
+
+  if (sym.is_local())
+    out_esym.st_bind = STB_LOCAL;
+  else if (sym.is_weak)
+    out_esym.st_bind = STB_WEAK;
+  else if (sym.file->is_dso)
+    out_esym.st_bind = STB_GLOBAL;
+  else
+    out_esym.st_bind = sym.esym().st_bind;
+
+  out_esym.st_name = strtab_offset;
+
+  if (sym.has_copyrel) {
+    out_esym.st_shndx = sym.copyrel_readonly
+                    ? ctx.copyrel_relro->shndx : ctx.copyrel->shndx;
+    out_esym.st_value = sym.get_addr(ctx);
+  } else if (sym.file->is_dso || sym.esym().is_undef()) {
+    out_esym.st_shndx = SHN_UNDEF;
+    out_esym.st_size = 0;
+    out_esym.st_value = sym.is_canonical ? sym.get_plt_addr(ctx) : 0;
+  } else if (sym.shndx < 0) {
+    // Internal file
+    out_esym.st_shndx = -sym.shndx;
+    out_esym.st_value = sym.get_addr(ctx);
+  } else {
+    InputSection<E> *isec = sym.get_input_section();
+    if (!isec) {
+      out_esym.st_shndx = SHN_ABS;
+      out_esym.st_value = sym.get_addr(ctx);
+    } else if (sym.get_type() == STT_TLS) {
+      out_esym.st_shndx = isec->output_section->shndx;
+      out_esym.st_value = sym.get_addr(ctx) - ctx.tls_begin;
+    } else {
+      out_esym.st_shndx = isec->output_section->shndx;
+      out_esym.st_value = sym.get_addr(ctx, false);
+      out_esym.st_visibility = sym.visibility;
+    }
+  }
+}
+
 template <typename E>
 void DynsymSection<E>::add_symbol(Context<E> &ctx, Symbol<E> *sym) {
   if (symbols.empty())
@@ -1249,13 +1287,9 @@ void DynsymSection<E>::finalize(Context<
     vec[i].idx = i;
   });
 
-  auto is_local = [](Symbol<E> *sym) {
-    return !sym->is_imported && !sym->is_exported;
-  };
-
   tbb::parallel_sort(vec.begin() + 1, vec.end(), [&](const T &a, const T &b) {
-    return std::tuple(!is_local(a.sym), (bool)a.sym->is_exported, a.hash, a.idx) <
-           std::tuple(!is_local(b.sym), (bool)b.sym->is_exported, b.hash, b.idx);
+    return std::tuple(!a.sym->is_local(), (bool)a.sym->is_exported, a.hash, a.idx) <
+           std::tuple(!b.sym->is_local(), (bool)b.sym->is_exported, b.hash, b.idx);
   });
 
   ctx.dynstr->dynsym_offset = ctx.dynstr->shdr.sh_size;
@@ -1268,7 +1302,9 @@ void DynsymSection<E>::finalize(Context<
 
   // ELF's symbol table sh_info holds the offset of the first global symbol.
   auto first_global =
-    std::partition_point(symbols.begin() + 1, symbols.end(), is_local);
+    std::partition_point(symbols.begin() + 1, symbols.end(), [](Symbol<E> *sym) {
+      return sym->is_local();
+    });
   this->shdr.sh_info = first_global - symbols.begin();
 }
 
@@ -1289,44 +1325,11 @@ void DynsymSection<E>::copy_buf(Context<
     ElfSym<E> &esym =
       *(ElfSym<E> *)(base + sym.get_dynsym_idx(ctx) * sizeof(ElfSym<E>));
 
-    memset(&esym, 0, sizeof(esym));
-    esym.st_type = sym.esym().st_type;
-    esym.st_size = sym.esym().st_size;
-
-    if (i < this->shdr.sh_info)
-      esym.st_bind = STB_LOCAL;
-    else if (sym.is_weak)
-      esym.st_bind = STB_WEAK;
-    else if (sym.file->is_dso)
-      esym.st_bind = STB_GLOBAL;
-    else
-      esym.st_bind = sym.esym().st_bind;
-
-    esym.st_name = name_offset;
+    get_output_esym(ctx, sym, name_offset, esym);
     name_offset += sym.name().size() + 1;
 
-    if (sym.has_copyrel) {
-      esym.st_shndx = sym.copyrel_readonly
-        ? ctx.copyrel_relro->shndx : ctx.copyrel->shndx;
-      esym.st_value = sym.get_addr(ctx);
-    } else if (sym.file->is_dso || sym.esym().is_undef()) {
-      esym.st_shndx = SHN_UNDEF;
-      esym.st_size = 0;
-      esym.st_value = sym.is_canonical ? sym.get_plt_addr(ctx) : 0;
-    } else {
-      InputSection<E> *isec = sym.get_input_section();
-      if (!isec) {
-        esym.st_shndx = SHN_ABS;
-        esym.st_value = sym.get_addr(ctx);
-      } else if (sym.get_type() == STT_TLS) {
-        esym.st_shndx = isec->output_section->shndx;
-        esym.st_value = sym.get_addr(ctx) - ctx.tls_begin;
-      } else {
-        esym.st_shndx = isec->output_section->shndx;
-        esym.st_value = sym.get_addr(ctx, false);
-        esym.st_visibility = sym.visibility;
-      }
-    }
+    if (esym.st_bind == STB_LOCAL)
+      assert(i < this->shdr.sh_info);
   }
 }
 
diff -pruN 1.3.1+dfsg-1/elf/passes.cc 1.4.0+dfsg-1/elf/passes.cc
--- 1.3.1+dfsg-1/elf/passes.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/passes.cc	2022-08-04 12:47:21.000000000 +0000
@@ -406,11 +406,13 @@ ObjectFile<E> *create_internal_file(Cont
     ctx.etext = add("etext");
   if (!get_symbol(ctx, "edata")->file)
     ctx.edata = add("edata");
+  if (!get_symbol(ctx, "__dso_handle")->file)
+    ctx.__dso_handle = add("__dso_handle");
 
   if constexpr (E::supports_tlsdesc)
     ctx._TLS_MODULE_BASE_ = add("_TLS_MODULE_BASE_");
 
-  if constexpr (std::is_same_v<E, RISCV64>)
+  if constexpr (std::is_same_v<E, RISCV64> || std::is_same_v<E, RISCV32>)
     if (!ctx.arg.shared)
       ctx.__global_pointer = add("__global_pointer$");
 
@@ -920,21 +922,21 @@ void scan_rels(Context<E> &ctx) {
     if (sym->flags & NEEDS_GOT)
       ctx.got->add_got_symbol(ctx, sym);
 
-    if (sym->flags & NEEDS_PLT) {
-      if (sym->is_canonical) {
-        // A canonical PLT needs to be visible from DSOs.
-        sym->is_exported = true;
+    if (sym->flags & NEEDS_CPLT) {
+      sym->is_canonical = true;
+
+      // A canonical PLT needs to be visible from DSOs.
+      sym->is_exported = true;
 
-        // We can't use .plt.got for a canonical PLT because otherwise
-        // .plt.got and .got would refer each other, resulting in an
-        // infinite loop at runtime.
+      // We can't use .plt.got for a canonical PLT because otherwise
+      // .plt.got and .got would refer each other, resulting in an
+      // infinite loop at runtime.
+      ctx.plt->add_symbol(ctx, sym);
+    } else if (sym->flags & NEEDS_PLT) {
+      if (sym->flags & NEEDS_GOT)
+        ctx.pltgot->add_symbol(ctx, sym);
+      else
         ctx.plt->add_symbol(ctx, sym);
-      } else {
-        if (sym->flags & NEEDS_GOT)
-          ctx.pltgot->add_symbol(ctx, sym);
-        else
-          ctx.plt->add_symbol(ctx, sym);
-      }
     }
 
     if (sym->flags & NEEDS_GOTTP)
@@ -1079,9 +1081,6 @@ void apply_version_script(Context<E> &ct
     }
   }
 
-  matcher.compile();
-  cpp_matcher.compile();
-
   tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
     for (Symbol<E> *sym : file->get_global_syms()) {
       if (sym->file != file)
@@ -1094,9 +1093,12 @@ void apply_version_script(Context<E> &ct
         continue;
       }
 
-      if (!cpp_matcher.empty())
-        if (std::optional<u16> ver = cpp_matcher.find(demangle(name)))
+      if (!cpp_matcher.empty()) {
+        if (std::optional<std::string_view> s = cpp_demangle(name))
+          name = *s;
+        if (std::optional<u16> ver = cpp_matcher.find(name))
           sym->ver_idx = *ver;
+      }
     }
   });
 }
@@ -1459,7 +1461,6 @@ void fix_synthetic_symbols(Context<E> &c
   if (Chunk<E> *chunk = find(".bss"))
     start(ctx.__bss_start, chunk);
 
-  // __ehdr_start and __executable_start
   if (ctx.ehdr) {
     for (Chunk<E> *chunk : ctx.chunks) {
       if (chunk->shndx == 1) {
@@ -1467,6 +1468,11 @@ void fix_synthetic_symbols(Context<E> &c
         ctx.__ehdr_start->value = ctx.ehdr->shdr.sh_addr;
         ctx.__executable_start->shndx = -1;
         ctx.__executable_start->value = ctx.ehdr->shdr.sh_addr;
+
+        if (ctx.__dso_handle) {
+          ctx.__dso_handle->shndx = -1;
+          ctx.__dso_handle->value = ctx.ehdr->shdr.sh_addr;
+        }
         break;
       }
     }
@@ -1567,7 +1573,7 @@ void fix_synthetic_symbols(Context<E> &c
   start(ctx.__GNU_EH_FRAME_HDR, ctx.eh_frame_hdr);
 
   // RISC-V's __global_pointer$
-  if constexpr (std::is_same_v<E, RISCV64>) {
+  if constexpr (std::is_same_v<E, RISCV64> || std::is_same_v<E, RISCV32>) {
     if (!ctx.arg.shared) {
       if (Chunk<E> *chunk = find(".sdata"))
         ctx.__global_pointer->shndx = -chunk->shndx;
diff -pruN 1.3.1+dfsg-1/elf/relocatable.cc 1.4.0+dfsg-1/elf/relocatable.cc
--- 1.3.1+dfsg-1/elf/relocatable.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/elf/relocatable.cc	2022-08-04 12:47:21.000000000 +0000
@@ -433,7 +433,9 @@ open_files(Context<E> &ctx, std::span<st
     }
 
     if (arg.starts_with("--version-script=") ||
-        arg.starts_with("--dynamic-list="))
+        arg.starts_with("--dynamic-list=") ||
+        arg.starts_with("--export-dynamic-symbol=") ||
+        arg.starts_with("--export-dynamic-symbol-list="))
       continue;
 
     MappedFile<Context<E>> *mf = nullptr;
diff -pruN 1.3.1+dfsg-1/filetype.h 1.4.0+dfsg-1/filetype.h
--- 1.3.1+dfsg-1/filetype.h	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/filetype.h	2022-08-04 12:47:21.000000000 +0000
@@ -11,7 +11,9 @@ enum class FileType {
   ELF_OBJ,
   ELF_DSO,
   MACH_OBJ,
+  MACH_EXE,
   MACH_DYLIB,
+  MACH_BUNDLE,
   MACH_UNIVERSAL,
   AR,
   THIN_AR,
@@ -98,8 +100,12 @@ FileType get_file_type(MappedFile<C> *mf
     switch (*(ul32 *)(data.data() + 12)) {
     case 1: // MH_OBJECT
       return FileType::MACH_OBJ;
+    case 2: // MH_EXECUTE
+      return FileType::MACH_EXE;
     case 6: // MH_DYLIB
       return FileType::MACH_DYLIB;
+    case 8: // MH_BUNDLE
+      return FileType::MACH_BUNDLE;
     }
     return FileType::UNKNOWN;
   }
@@ -127,8 +133,10 @@ inline std::string filetype_to_string(Fi
   case FileType::EMPTY: return "EMPTY";
   case FileType::ELF_OBJ: return "ELF_OBJ";
   case FileType::ELF_DSO: return "ELF_DSO";
+  case FileType::MACH_EXE: return "MACH_EXE";
   case FileType::MACH_OBJ: return "MACH_OBJ";
   case FileType::MACH_DYLIB: return "MACH_DYLIB";
+  case FileType::MACH_BUNDLE: return "MACH_BUNDLE";
   case FileType::MACH_UNIVERSAL: return "MACH_UNIVERSAL";
   case FileType::AR: return "AR";
   case FileType::THIN_AR: return "THIN_AR";
diff -pruN 1.3.1+dfsg-1/.github/workflows/ci.yml 1.4.0+dfsg-1/.github/workflows/ci.yml
--- 1.3.1+dfsg-1/.github/workflows/ci.yml	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/.github/workflows/ci.yml	2022-08-04 12:47:21.000000000 +0000
@@ -19,8 +19,8 @@ jobs:
     steps:
     - uses: actions/checkout@v2
     - uses: rui314/setup-mold@staging
-    - name: apt-get
-      run: sudo apt-get update && sudo apt-get install git build-essential libstdc++-10-dev cmake clang libssl-dev zlib1g-dev libtbb-dev git bsdmainutils dwarfdump
+    - name: install-build-deps
+      run: sudo apt-get update && sudo ./install-build-deps.sh
     - name: ccache
       uses: hendrikmuhs/ccache-action@v1
     - name: make test
@@ -31,9 +31,12 @@ jobs:
     container: gcc:11.1.0
     steps:
     - uses: actions/checkout@v2
-    - name: apt-get
+    - name: install-build-deps
       run: |
-        dpkg --add-architecture i386 && apt-get update && apt-get install -y sudo libssl-dev zlib1g-dev cmake clang bsdmainutils dwarfdump qemu-user gcc-10-i686-linux-gnu gcc-10-aarch64-linux-gnu gcc-10-riscv64-linux-gnu gcc-10-arm-linux-gnueabihf g++-10-i686-linux-gnu g++-10-aarch64-linux-gnu g++-10-riscv64-linux-gnu g++-10-arm-linux-gnueabihf
+        dpkg --add-architecture i386
+        apt-get update
+        ./install-build-deps.sh
+        apt-get install -y sudo qemu-user gcc-10-i686-linux-gnu gcc-10-aarch64-linux-gnu gcc-10-riscv64-linux-gnu gcc-10-arm-linux-gnueabihf g++-10-i686-linux-gnu g++-10-aarch64-linux-gnu g++-10-riscv64-linux-gnu g++-10-arm-linux-gnueabihf
         ln -sf /usr/bin/i686-linux-gnu-gcc-10 /usr/bin/i686-linux-gnu-gcc
         ln -sf /usr/bin/i686-linux-gnu-g++-10 /usr/bin/i686-linux-gnu-g++
         ln -sf /usr/bin/aarch64-linux-gnu-gcc-10 /usr/bin/aarch64-linux-gnu-gcc
diff -pruN 1.3.1+dfsg-1/install-build-deps.sh 1.4.0+dfsg-1/install-build-deps.sh
--- 1.3.1+dfsg-1/install-build-deps.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/install-build-deps.sh	2022-08-04 12:47:21.000000000 +0000
@@ -6,12 +6,12 @@ source /etc/os-release
 # The second line installs extra packages for `make test`.
 
 case "$ID$VERSION_ID" in
-ubuntu20.04)
-  apt install -y git cmake libssl-dev zlib1g-dev gcc g++ g++-10
+ubuntu20.*)
+  apt-get install -y git cmake libssl-dev zlib1g-dev gcc g++ g++-10
   apt-get install -y file bsdmainutils
   ;;
-ubuntu22.04 | debian11)
-  apt install -y git cmake libssl-dev zlib1g-dev gcc g++
+ubuntu22.* | debian11)
+  apt-get install -y git cmake libssl-dev zlib1g-dev gcc g++
   apt-get install -y file bsdmainutils
   ;;
 fedora*)
@@ -29,6 +29,9 @@ opensuse-tumbleweed*)
 gentoo*)
   emerge dev-vcs/git dev-util/cmake sys-libs/zlib
   ;;
+arch*)
+  pacman -S --needed --noconfirm base-devel zlib openssl cmake util-linux git
+  ;;
 *)
   echo "Error: don't know anything about build dependencies on $ID $VERSION_ID"
   exit 1
diff -pruN 1.3.1+dfsg-1/macho/arch-arm64.cc 1.4.0+dfsg-1/macho/arch-arm64.cc
--- 1.3.1+dfsg-1/macho/arch-arm64.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/macho/arch-arm64.cc	2022-08-04 12:47:21.000000000 +0000
@@ -82,59 +82,62 @@ void StubHelperSection<E>::copy_buf(Cont
   }
 }
 
-static Relocation<E>
-read_reloc(Context<E> &ctx, ObjectFile<E> &file,
-           const MachSection &hdr, MachRel *rels, i64 &idx) {
-  i64 addend = 0;
-
-  switch (rels[idx].type) {
-  case ARM64_RELOC_UNSIGNED:
-  case ARM64_RELOC_SUBTRACTOR:
-    switch (MachRel &r = rels[idx]; r.p2size) {
-    case 2:
-      addend = *(il32 *)((u8 *)file.mf->data + hdr.offset + r.offset);
+template <>
+std::vector<Relocation<E>>
+read_relocations(Context<E> &ctx, ObjectFile<E> &file, const MachSection &hdr) {
+  std::vector<Relocation<E>> vec;
+  vec.reserve(hdr.nreloc);
+
+  MachRel *rels = (MachRel *)(file.mf->data + hdr.reloff);
+
+  for (i64 i = 0; i < hdr.nreloc; i++) {
+    i64 addend = 0;
+
+    switch (rels[i].type) {
+    case ARM64_RELOC_UNSIGNED:
+    case ARM64_RELOC_SUBTRACTOR:
+      switch (rels[i].p2size) {
+      case 2:
+        addend = *(il32 *)((u8 *)file.mf->data + hdr.offset + rels[i].offset);
+        break;
+      case 3:
+        addend = *(il64 *)((u8 *)file.mf->data + hdr.offset + rels[i].offset);
+        break;
+      default:
+        unreachable();
+      }
       break;
-    case 3:
-      addend = *(il64 *)((u8 *)file.mf->data + hdr.offset + r.offset);
+    case ARM64_RELOC_ADDEND:
+      addend = rels[i++].idx;
       break;
-    default:
-      unreachable();
     }
-    break;
-  case ARM64_RELOC_ADDEND:
-    addend = rels[idx++].idx;
-    break;
-  }
 
-  MachRel &r = rels[idx];
-  Relocation<E> rel{r.offset, (u8)r.type, (u8)r.p2size, (bool)r.is_pcrel};
+    MachRel &r = rels[i];
+    vec.push_back({r.offset, (u8)r.type, (u8)r.p2size});
 
-  if (r.is_extern) {
-    rel.sym = file.syms[r.idx];
-    rel.addend = addend;
-    return rel;
-  }
+    Relocation<E> &rel = vec.back();
 
-  u64 addr = r.is_pcrel ? (hdr.addr + r.offset + addend) : addend;
-  Subsection<E> *target = file.find_subsection(ctx, addr);
-  if (!target)
-    Fatal(ctx) << file << ": bad relocation: " << r.offset;
+    if (i > 0 && rels[i - 1].type == ARM64_RELOC_SUBTRACTOR)
+      rel.is_subtracted = true;
 
-  rel.subsec = target;
-  rel.addend = addr - target->input_addr;
-  return rel;
-}
+    if (!rel.is_subtracted && rels[i].type != ARM64_RELOC_SUBTRACTOR)
+      rel.is_pcrel = r.is_pcrel;
 
-template <>
-std::vector<Relocation<E>>
-read_relocations(Context<E> &ctx, ObjectFile<E> &file,
-                 const MachSection &hdr) {
-  std::vector<Relocation<E>> vec;
-  vec.reserve(hdr.nreloc);
+    if (r.is_extern) {
+      rel.sym = file.syms[r.idx];
+      rel.addend = addend;
+      continue;
+    }
+
+    u64 addr = r.is_pcrel ? (hdr.addr + r.offset + addend) : addend;
+    Subsection<E> *target = file.find_subsection(ctx, addr);
+    if (!target)
+      Fatal(ctx) << file << ": bad relocation: " << r.offset;
+
+    rel.subsec = target;
+    rel.addend = addr - target->input_addr;
+  }
 
-  MachRel *rels = (MachRel *)(file.mf->data + hdr.reloff);
-  for (i64 i = 0; i < hdr.nreloc; i++)
-    vec.push_back(read_reloc(ctx, file, hdr, rels, i));
   return vec;
 }
 
@@ -463,4 +466,229 @@ void RangeExtensionThunk<E>::copy_buf(Co
   }
 }
 
+#define ASSERT_RANGE(val, start, size)                          \
+  assert((start) <= (val) && (val) < ((start) + (size)))
+
+// Rewrite
+//   ldr Xb/Wb, [Xa, #imm]
+// with
+//   ldr Xb/Wb, [#base_addr + #imm]
+// if we can.
+static bool relax_ldr(ul32 *loc, u64 loc_addr, u64 base_addr) {
+  switch (*loc & 0xffc0'0000) {
+  case 0xf940'0000: { // ldr Xb, [Xa, #imm]
+    u64 imm = bits(*loc, 21, 10) * 8;
+    i64 disp = base_addr + imm - loc_addr;
+    if (disp == sign_extend(disp, 20) && (disp & 0b11) == 0) {
+      // ldr Xc, [#base_addr + #imm]
+      *loc = 0x5800'0000 | (bits(disp, 20, 2) << 5) | bits(*loc, 4, 0);
+      return true;
+    }
+    return false;
+  }
+  case 0xb940'0000: { // ldr Wb, [Xa, #imm]
+    u64 imm = bits(*loc, 21, 10) * 4;
+    i64 disp = base_addr + imm - loc_addr;
+    if (disp == sign_extend(disp, 20) && (disp & 0b11) == 0) {
+      // ldr Wc, [#base_addr + #imm]
+      *loc = 0x1800'0000 | (bits(disp, 20, 2) << 5) | bits(*loc, 4, 0);
+      return true;
+    }
+    return false;
+  }
+  }
+  return false;
+}
+
+static bool is_adrp(ul32 *loc) {
+  return (*loc & 0x9f00'0000) == 0x9000'0000;
+}
+
+static u64 get_adrp_imm(ul32 *loc) {
+  return (bits(*loc, 23, 5) << 14) + (bits(*loc, 30, 29) << 12);
+}
+
+static void relax_adrp_ldr_got_ldr(Context<E> &ctx,
+                                   ul32 *loc1, ul32 *loc2, ul32 *loc3,
+                                   u64 addr1, u64 addr2, u64 addr3) {
+  // We expect the following instructions for a GOT-indirect load:
+  //
+  //   adrp Xa, _foo@GOTPAGE
+  //   ldr  Xb, [Xa, _foo@GOTPAGEOFF]
+  //   ldr/ldrb/ldrsb/ldrsh/... Xc/Wc, [Xb, #imm]
+  if (!is_adrp(loc1) || (*loc2 & 0xffc0'0000) != 0xf940'0000)
+    return;
+
+  u64 got_page = page(addr1) + get_adrp_imm(loc1);
+  u64 pageoff = bits(*loc2, 21, 10) << 3;
+
+  ASSERT_RANGE(got_page + pageoff, ctx.got.hdr.addr, ctx.got.hdr.size);
+
+  u64 got_value = *(ul64 *)(ctx.buf + ctx.got.hdr.offset + got_page + pageoff -
+                            ctx.got.hdr.addr);
+
+  // If the GOT slot is already filled with a value and we can
+  // inline the value, do it.
+  if (got_value && relax_ldr(loc3, addr3, got_value)) {
+    *loc1 = 0xd503'201f; // nop
+    *loc2 = 0xd503'201f; // nop
+    return;
+  }
+
+  // If the GOT slot is close enough to PC, we can eliminate ADRP.
+  if (relax_ldr(loc2, addr2, got_page))
+    *loc1 = 0xd503'201f; // nop
+}
+
+static void relax_adrp_adrp(Context<E> &ctx, ul32 *loc1, ul32 *loc2,
+                            u64 addr1, u64 addr2) {
+  //   adrp Xa, _foo@PAGE
+  //   adrp Xa, _bar@PAGE
+  // ->
+  //   ldr  Xa, _foo@PAGE
+  //   nop
+
+  if (is_adrp(loc1) && is_adrp(loc2) &&
+      page(addr1) + get_adrp_imm(loc1) == page(addr2) + get_adrp_imm(loc2) &&
+      bits(*loc1, 4, 0) == bits(*loc2, 4, 0))
+    *loc2 = 0xd503'201f; // nop
+}
+
+static void relax_adrp_ldr(Context<E> &ctx, ul32 *loc1, ul32 *loc2,
+                           u64 addr1, u64 addr2) {
+  //   adrp Xa, _foo@PAGE
+  //   ldr  Xb/Wb, [Xa, _foo@PAGEOFF]
+  // ->
+  //   nop
+  //   ldr  Xb/Wb, [_foo]
+  if (is_adrp(loc1) && relax_ldr(loc2, addr2, page(addr1) + get_adrp_imm(loc1)))
+    *loc1 = 0xd503'201f; // nop
+}
+
+static void relax_adrp_add(Context<E> &ctx, ul32 *loc1, ul32 *loc2,
+                           u64 addr1, u64 addr2) {
+  //   adrp Xa, _foo@PAGE
+  //   add  Xb, Xa, _foo@PAGEOFF
+  // ->
+  //   nop
+  //   adr  Xb, _foo
+  if (!is_adrp(loc1) || (*loc2 & 0xffc0'0000) != 0x9100'0000)
+    return;
+
+  i64 disp = page(addr1) + get_adrp_imm(loc1) + bits(*loc2, 21, 10) - addr2;
+
+  if (disp == sign_extend(disp, 20)) {
+    *loc1 = 0xd503'201f;                                  // nop
+    *loc2 = 0x1000'0000 | (bits(disp, 1, 0) << 29) |
+            (bits(disp, 20, 2) << 5) | bits(*loc2, 4, 0); // adr Xb, _foo
+  }
+}
+
+// On ARM, we generally need two or more instructions to materialize
+// an address of an object in a register or jump to a function.
+// However, if an object or a function is close enough to PC, a single
+// instruction suffices.
+//
+// This function replaces such redundant instruction sequences with
+// shorter instruction sequences. We pad holes with NOPs, so the total
+// number of instructions won't change by this relaxation, but replacing
+// instructions with NOPs generally makes program faster since the CPU
+// has a special logic to skip NOPs as quickly as possible.
+//
+// Locations of relaxable instructions are in the
+// LC_LINKER_OPTIMIZATION_HINT segment. That segment contains a
+// sequence of ULEB-encoded integers.
+//
+// This pass is optional; an output file works correctly without
+// applying these optimizations.
+void apply_linker_optimization_hints(Context<E> &ctx) {
+  Timer t(ctx, "apply_linker_optimization_hints");
+
+  tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
+    std::string_view hints = file->get_linker_optimization_hints(ctx);
+
+    while (!hints.empty()) {
+      i64 type = read_uleb(hints);
+      if (type == 0)
+        return;
+
+      i64 nargs = read_uleb(hints);
+
+      switch (type) {
+      case LOH_ARM64_ADRP_LDR_GOT_LDR: {
+        assert(nargs == 3);
+        i64 input_addr1 = read_uleb(hints);
+        i64 input_addr2 = read_uleb(hints);
+        i64 input_addr3 = read_uleb(hints);
+
+        Subsection<E> *subsec = file->find_subsection(ctx, input_addr1);
+        if (!subsec || !subsec->is_alive)
+          return;
+
+        ASSERT_RANGE(input_addr1, subsec->input_addr, subsec->input_size);
+        ASSERT_RANGE(input_addr2, subsec->input_addr, subsec->input_size);
+        ASSERT_RANGE(input_addr3, subsec->input_addr, subsec->input_size);
+
+        i64 offset1 = subsec->input_addr - input_addr1;
+        i64 offset2 = subsec->input_addr - input_addr2;
+        i64 offset3 = subsec->input_addr - input_addr3;
+
+        u8 *loc = ctx.buf + subsec->isec.osec.hdr.offset + subsec->output_offset;
+        ul32 *loc1 = (ul32 *)(loc + offset1);
+        ul32 *loc2 = (ul32 *)(loc + offset2);
+        ul32 *loc3 = (ul32 *)(loc + offset3);
+
+        u64 addr1 = subsec->get_addr(ctx) + offset1;
+        u64 addr2 = subsec->get_addr(ctx) + offset2;
+        u64 addr3 = subsec->get_addr(ctx) + offset3;
+
+        relax_adrp_ldr_got_ldr(ctx, loc1, loc2, loc3, addr1, addr2, addr3);
+        break;
+      }
+      case LOH_ARM64_ADRP_ADRP:
+      case LOH_ARM64_ADRP_LDR:
+      case LOH_ARM64_ADRP_ADD: {
+        assert(nargs == 2);
+        i64 input_addr1 = read_uleb(hints);
+        i64 input_addr2 = read_uleb(hints);
+
+        Subsection<E> *subsec = file->find_subsection(ctx, input_addr1);
+        if (!subsec || !subsec->is_alive)
+          return;
+
+        ASSERT_RANGE(input_addr1, subsec->input_addr, subsec->input_size);
+        ASSERT_RANGE(input_addr2, subsec->input_addr, subsec->input_size);
+
+        i64 offset1 = subsec->input_addr - input_addr1;
+        i64 offset2 = subsec->input_addr - input_addr2;
+
+        u8 *loc = ctx.buf + subsec->isec.osec.hdr.offset + subsec->output_offset;
+        ul32 *loc1 = (ul32 *)(loc + offset1);
+        ul32 *loc2 = (ul32 *)(loc + offset2);
+
+        u64 addr1 = subsec->get_addr(ctx) + offset1;
+        u64 addr2 = subsec->get_addr(ctx) + offset2;
+
+        switch (type) {
+        case LOH_ARM64_ADRP_ADRP:
+          relax_adrp_adrp(ctx, loc1, loc2, addr1, addr2);
+          break;
+        case LOH_ARM64_ADRP_LDR:
+          relax_adrp_ldr(ctx, loc1, loc2, addr1, addr2);
+          break;
+        case LOH_ARM64_ADRP_ADD:
+          relax_adrp_add(ctx, loc1, loc2, addr1, addr2);
+          break;
+        }
+        break;
+      }
+      default:
+        // Skip unsupported optimizations hints.
+        for (i64 i = 0; i < nargs; i++)
+          read_uleb(hints);
+      }
+    }
+  });
+}
+
 } // namespace mold::macho
diff -pruN 1.3.1+dfsg-1/macho/arch-x86-64.cc 1.4.0+dfsg-1/macho/arch-x86-64.cc
--- 1.3.1+dfsg-1/macho/arch-x86-64.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/macho/arch-x86-64.cc	2022-08-04 12:47:21.000000000 +0000
@@ -79,45 +79,6 @@ static i64 read_addend(u8 *buf, const Ma
   unreachable();
 }
 
-static Relocation<E>
-read_reloc(Context<E> &ctx, ObjectFile<E> &file,
-           const MachSection &hdr, MachRel &r) {
-  if (r.p2size != 2 && r.p2size != 3)
-    Fatal(ctx) << file << ": invalid r.p2size: " << (u32)r.p2size;
-
-  if (r.is_pcrel) {
-    if (r.p2size != 2)
-      Fatal(ctx) << file << ": invalid PC-relative reloc: " << r.offset;
-  } else {
-    if (r.p2size != 3)
-      Fatal(ctx) << file << ": invalid non-PC-relative reloc: " << r.offset;
-  }
-
-  u8 *buf = (u8 *)file.mf->data + hdr.offset;
-  Relocation<E> rel{r.offset, (u8)r.type, (u8)r.p2size, (bool)r.is_pcrel};
-  i64 addend = read_addend(buf, r);
-
-  if (r.is_extern) {
-    rel.sym = file.syms[r.idx];
-    rel.addend = addend;
-    return rel;
-  }
-
-  u32 addr;
-  if (r.is_pcrel)
-    addr = hdr.addr + r.offset + 4 + addend;
-  else
-    addr = addend;
-
-  Subsection<E> *target = file.find_subsection(ctx, addr);
-  if (!target)
-    Fatal(ctx) << file << ": bad relocation: " << r.offset;
-
-  rel.subsec = target;
-  rel.addend = addr - target->input_addr;
-  return rel;
-}
-
 template <>
 std::vector<Relocation<E>>
 read_relocations(Context<E> &ctx, ObjectFile<E> &file,
@@ -126,8 +87,35 @@ read_relocations(Context<E> &ctx, Object
   vec.reserve(hdr.nreloc);
 
   MachRel *rels = (MachRel *)(file.mf->data + hdr.reloff);
-  for (i64 i = 0; i < hdr.nreloc; i++)
-    vec.push_back(read_reloc(ctx, file, hdr, rels[i]));
+
+  for (i64 i = 0; i < hdr.nreloc; i++) {
+    MachRel &r = rels[i];
+    i64 addend = read_addend((u8 *)file.mf->data + hdr.offset, r);
+
+    vec.push_back({r.offset, (u8)r.type, (u8)r.p2size});
+    Relocation<E> &rel = vec.back();
+
+    if (i > 0 && rels[i - 1].type == X86_64_RELOC_SUBTRACTOR)
+      rel.is_subtracted = true;
+
+    if (!rel.is_subtracted && rels[i].type != X86_64_RELOC_SUBTRACTOR)
+      rel.is_pcrel = r.is_pcrel;
+
+    if (r.is_extern) {
+      rel.sym = file.syms[r.idx];
+      rel.addend = addend;
+      continue;
+    }
+
+    u64 addr = r.is_pcrel ? (hdr.addr + r.offset + addend + 4) : addend;
+    Subsection<E> *target = file.find_subsection(ctx, addr);
+    if (!target)
+      Fatal(ctx) << file << ": bad relocation: " << r.offset;
+
+    rel.subsec = target;
+    rel.addend = addr - target->input_addr;
+  }
+
   return vec;
 }
 
@@ -143,6 +131,7 @@ void Subsection<E>::scan_relocations(Con
 
     switch (r.type) {
     case X86_64_RELOC_UNSIGNED:
+    case X86_64_RELOC_SUBTRACTOR:
       if (sym->is_imported) {
         if (r.p2size != 3) {
           Error(ctx) << this->isec << ": " << r << " relocation at offset 0x"
@@ -168,14 +157,17 @@ void Subsection<E>::scan_relocations(Con
 
 template <>
 void Subsection<E>::apply_reloc(Context<E> &ctx, u8 *buf) {
-  for (const Relocation<E> &r : get_rels()) {
+  std::span<Relocation<E>> rels = get_rels();
+
+  for (i64 i = 0; i < rels.size(); i++) {
+    Relocation<E> &r = rels[i];
+    u64 val = r.addend;
+
     if (r.sym && !r.sym->file) {
       Error(ctx) << "undefined symbol: " << isec.file << ": " << *r.sym;
       continue;
     }
 
-    u64 val = r.addend;
-
     switch (r.type) {
     case X86_64_RELOC_UNSIGNED:
     case X86_64_RELOC_SIGNED:
@@ -185,6 +177,15 @@ void Subsection<E>::apply_reloc(Context<
     case X86_64_RELOC_SIGNED_4:
       val += r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
       break;
+    case X86_64_RELOC_SUBTRACTOR: {
+      Relocation<E> s = rels[++i];
+      assert(s.type == X86_64_RELOC_UNSIGNED);
+      assert(r.p2size == s.p2size);
+      u64 val1 = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
+      u64 val2 = s.sym ? s.sym->get_addr(ctx) : s.subsec->get_addr(ctx);
+      val += val2 - val1;
+      break;
+    }
     case X86_64_RELOC_GOT:
     case X86_64_RELOC_GOT_LOAD:
       val += r.sym->get_got_addr(ctx);
@@ -196,13 +197,13 @@ void Subsection<E>::apply_reloc(Context<
       Fatal(ctx) << isec << ": unknown reloc: " << (int)r.type;
     }
 
-    // An address of a thread-local variable is computed as an offset
-    // to the beginning of the first thread-local section.
-    if (isec.hdr.type == S_THREAD_LOCAL_VARIABLES)
+    if (isec.hdr.type == S_THREAD_LOCAL_VARIABLES) {
+      // An address of a thread-local variable is computed as an offset
+      // to the beginning of the first thread-local section.
       val -= ctx.tls_begin;
-
-    if (r.is_pcrel)
+    } else if (r.is_pcrel) {
       val -= get_addr(ctx) + r.offset + 4 + get_reloc_addend(r.type);
+    }
 
     if (r.p2size == 2)
       *(ul32 *)(buf + r.offset) = val;
diff -pruN 1.3.1+dfsg-1/macho/cmdline.cc 1.4.0+dfsg-1/macho/cmdline.cc
--- 1.3.1+dfsg-1/macho/cmdline.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/macho/cmdline.cc	2022-08-04 12:47:21.000000000 +0000
@@ -20,6 +20,7 @@ Options:
   -U <SYMBOL>                 Allow a symbol to be undefined
   -Z                          Do not search the standard directories when
                               searching for libraries and frameworks
+  -add_ast_path <FILE>        Add a N_AST symbol with the given filename
   -add_empty_section <SEGNAME> <SECTNAME>
                               Add an empty section
   -adhoc_codesign             Add ad-hoc code signature to the output file
@@ -30,6 +31,7 @@ Options:
     -no_application_extension
   -arch <ARCH_NAME>           Specify target architecture
   -bundle                     Produce a mach-o bundle
+  -bundle_loader <EXECUTABLE> Resolve undefined symbols using the given executable
   -compatibility_version <VERSION>
                               Specifies the compatibility version number of the library
   -current_version <VERSION>  Specifies the current version number of the library.
@@ -61,15 +63,18 @@ Options:
                               Allocate MAXPATHLEN byte padding after load commands
   -help                       Report usage information
   -hidden-l<LIB>
+  -ignore_optimization_hints  Do not rewrite instructions as optimization
   -install_name <NAME>
   -l<LIB>                     Search for a given library
   -lto_library <FILE>         Ignored
   -macos_version_min <VERSION>
   -map <FILE>                 Write map file to a given file
+  -mark_dead_strippable_dylib Mark the output as dead-strippable
   -needed-l<LIB>              Search for a given library
   -needed-framework <NAME>[,<SUFFIX>]
                               Search for a given framework
   -no_deduplicate             Ignored
+  -no_function_starts         Do not generate an LC_FUNCTION_STARTS load command
   -no_uuid                    Do not generate an LC_UUID load command
   -o <FILE>                   Set output filename
   -objc_abi_version <VERSION> Ignored
@@ -92,7 +97,7 @@ Options:
   -t                          Print out each file the linker loads
   -thread_count <NUMBER>      Use given number of threads
   -u <SYMBOL>                 Force load a given symbol from archive if necessary
-  -uexported_symbol <SYMBOL>  Export all but a given symbol
+  -unexported_symbol <SYMBOL> Export all but a given symbol
   -unexported_symbols_list <FILE>
                               Read a list of unexported symbols from a given file
   -v                          Report version information
@@ -297,6 +302,8 @@ std::vector<std::string> parse_nonpositi
       ctx.arg.ObjC = true;
     } else if (read_arg("-U")) {
       ctx.arg.U.push_back(std::string(arg));
+    } else if (read_arg("-add_ast_path")) {
+      ctx.arg.add_ast_path.push_back(std::string(arg));
     } else if (read_arg2("-add_empty_section")) {
       ctx.arg.add_empty_section.push_back({arg, arg2});
     } else if (read_flag("-adhoc_codesign")) {
@@ -320,6 +327,8 @@ std::vector<std::string> parse_nonpositi
         Fatal(ctx) << "unknown -arch: " << arg;
     } else if (read_flag("-bundle")) {
       ctx.output_type = MH_BUNDLE;
+    } else if (read_arg("-bundle_loader")) {
+      ctx.arg.bundle_loader = arg;
     } else if (read_arg("-compatibility_version") ||
                read_arg("-dylib_compatibility_version")) {
       ctx.arg.compatibility_version = parse_version(ctx, arg);
@@ -337,6 +346,7 @@ std::vector<std::string> parse_nonpositi
     } else if (read_flag("-demangle")) {
       ctx.arg.demangle = true;
     } else if (read_arg("-dependency_info")) {
+      ctx.arg.dependency_info = arg;
     } else if (read_flag("-dylib")) {
       ctx.output_type = MH_DYLIB;
     } else if (read_hex("-headerpad")) {
@@ -379,6 +389,8 @@ std::vector<std::string> parse_nonpositi
     } else if (read_joined("-hidden-l")) {
       remaining.push_back("-hidden-l");
       remaining.push_back(std::string(arg));
+    } else if (read_flag("-ignore_optimization_hints")) {
+      ctx.arg.ignore_optimization_hints = true;
     } else if (read_arg("-install_name") || read_arg("-dylib_install_name")) {
       ctx.arg.install_name = arg;
     } else if (read_joined("-l")) {
@@ -386,6 +398,8 @@ std::vector<std::string> parse_nonpositi
       remaining.push_back(std::string(arg));
     } else if (read_arg("-map")) {
       ctx.arg.map = arg;
+    } else if (read_flag("-mark_dead_strippable_dylib")) {
+      ctx.arg.mark_dead_strippable_dylib = true;
     } else if (read_arg("-mllvm")) {
       ctx.arg.mllvm.push_back(std::string(arg));
     } else if (read_joined("-needed-l")) {
@@ -395,6 +409,8 @@ std::vector<std::string> parse_nonpositi
       remaining.push_back("-needed_framework");
       remaining.push_back(std::string(arg));
     } else if (read_flag("-no_deduplicate")) {
+    } else if (read_flag("-no_function_starts")) {
+      ctx.arg.function_starts = false;
     } else if (read_flag("-no_uuid")) {
       ctx.arg.uuid = UUID_NONE;
     } else if (read_arg("-o")) {
@@ -478,6 +494,9 @@ std::vector<std::string> parse_nonpositi
   if (ctx.arg.thread_count == 0)
     ctx.arg.thread_count = get_default_thread_count();
 
+  if (!ctx.arg.bundle_loader.empty() && ctx.output_type != MH_BUNDLE)
+    Fatal(ctx) << "-bundle_loader cannot be specified without -bundle";
+
   auto add_search_path = [&](std::vector<std::string> &vec, std::string path) {
     if (!path.starts_with('/') || ctx.arg.syslibroot.empty()) {
       if (is_directory(path))
@@ -532,9 +551,6 @@ std::vector<std::string> parse_nonpositi
   if (ctx.arg.uuid == UUID_RANDOM)
     memcpy(ctx.uuid, get_uuid_v4().data(), 16);
 
-  ctx.arg.exported_symbols_list.compile();
-  ctx.arg.unexported_symbols_list.compile();
-
   return remaining;
 }
 
diff -pruN 1.3.1+dfsg-1/macho/dead-strip.cc 1.4.0+dfsg-1/macho/dead-strip.cc
--- 1.3.1+dfsg-1/macho/dead-strip.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/macho/dead-strip.cc	2022-08-04 12:47:21.000000000 +0000
@@ -1,24 +1,48 @@
 #include "mold.h"
 
+#include <tbb/parallel_for_each.h>
+
 namespace mold::macho {
 
 template <typename E>
 static std::vector<Subsection<E> *> collect_root_set(Context<E> &ctx) {
+  Timer t(ctx, "collect_root_set");
+
   std::vector<Subsection<E> *> rootset;
 
-  auto mark = [&](Symbol<E> *sym) {
-    if (sym && sym->subsec)
+  auto add = [&](Symbol<E> *sym) {
+    if (sym->subsec)
       rootset.push_back(sym->subsec);
   };
 
-  mark(ctx.arg.entry);
+  auto keep = [&](Symbol<E> *sym) {
+    if (sym->no_dead_strip)
+      return true;
+    if (ctx.output_type == MH_DYLIB || ctx.output_type == MH_BUNDLE)
+      if (sym->scope == SCOPE_EXTERN || sym->referenced_dynamically)
+        return true;
+    return false;
+  };
+
+  for (ObjectFile<E> *file : ctx.objs) {
+    for (Symbol<E> *sym : file->syms)
+      if (sym->file == file && keep(sym))
+        add(sym);
 
-  if (ctx.output_type == MH_DYLIB || ctx.output_type == MH_BUNDLE)
-    for (ObjectFile<E> *file : ctx.objs)
-      for (Symbol<E> *sym : file->syms)
-        if (sym->file == file && sym->scope == SCOPE_EXTERN)
-          mark(sym);
+    for (Subsection<E> *subsec : file->subsections)
+      if (const MachSection &hdr = subsec->isec.hdr;
+          (hdr.attr & S_ATTR_NO_DEAD_STRIP) ||
+          hdr.type == S_MOD_INIT_FUNC_POINTERS ||
+          hdr.type == S_MOD_TERM_FUNC_POINTERS)
+        rootset.push_back(subsec);
+  }
 
+  for (std::string_view name : ctx.arg.u)
+    if (Symbol<E> *sym = get_symbol(ctx, name); sym->file)
+      add(sym);
+
+  add(ctx.arg.entry);
+  add(get_symbol(ctx, "dyld_stub_binder"));
   return rootset;
 }
 
@@ -49,7 +73,7 @@ template <typename E>
 static bool refers_live_subsection(Subsection<E> &subsec) {
   for (Relocation<E> &rel : subsec.get_rels()) {
     if (rel.sym) {
-      if (!rel.sym->subsec || rel.sym->subsec->is_alive)
+      if (rel.sym->subsec && rel.sym->subsec->is_alive)
         return true;
     } else {
       if (rel.subsec->is_alive)
@@ -61,9 +85,14 @@ static bool refers_live_subsection(Subse
 
 template <typename E>
 static void mark(Context<E> &ctx, const std::vector<Subsection<E> *> &rootset) {
-  for (ObjectFile<E> *file : ctx.objs)
+  Timer t(ctx, "mark");
+
+  tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
     for (Subsection<E> *subsec : file->subsections)
-      subsec->is_alive = false;
+      subsec->is_alive.store(false, std::memory_order_relaxed);
+  });
+
+  std::atomic_thread_fence(std::memory_order_seq_cst);
 
   for (Subsection<E> *subsec : rootset)
     visit(ctx, *subsec);
@@ -86,20 +115,25 @@ static void mark(Context<E> &ctx, const
 
 template <typename E>
 static void sweep(Context<E> &ctx) {
-  for (ObjectFile<E> *file : ctx.objs)
+  Timer t(ctx, "sweep");
+
+  tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
     for (Symbol<E> *&sym : file->syms)
       if (sym->file == file && sym->subsec && !sym->subsec->is_alive)
         sym = nullptr;
+  });
 
-  for (ObjectFile<E> *file : ctx.objs) {
-    std::erase_if(file->subsections, [](const Subsection<E> *subsec) {
+  tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
+    std::erase_if(file->subsections, [](Subsection<E> *subsec) {
       return !subsec->is_alive;
     });
-  }
+  });
 }
 
 template <typename E>
 void dead_strip(Context<E> &ctx) {
+  Timer t(ctx, "dead_strip");
+
   std::vector<Subsection<E> *> rootset = collect_root_set(ctx);
   mark(ctx, rootset);
   sweep(ctx);
diff -pruN 1.3.1+dfsg-1/macho/input-files.cc 1.4.0+dfsg-1/macho/input-files.cc
--- 1.3.1+dfsg-1/macho/input-files.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/macho/input-files.cc	2022-08-04 12:47:21.000000000 +0000
@@ -21,6 +21,7 @@ void InputFile<E>::clear_symbols() {
       sym->scope = SCOPE_LOCAL;
       sym->is_imported = false;
       sym->is_weak = false;
+      sym->no_dead_strip = false;
       sym->subsec = nullptr;
       sym->value = 0;
       sym->is_common = false;
@@ -80,32 +81,38 @@ void ObjectFile<E>::parse(Context<E> &ct
 
 template <typename E>
 void ObjectFile<E>::parse_sections(Context<E> &ctx) {
-  MachHeader &hdr = *(MachHeader *)this->mf->data;
-  u8 *p = this->mf->data + sizeof(hdr);
+  SegmentCommand *cmd = (SegmentCommand *)find_load_command(ctx, LC_SEGMENT_64);
+  if (!cmd)
+    return;
 
-  // Read all but a symtab section
-  for (i64 i = 0; i < hdr.ncmds; i++) {
-    LoadCommand &lc = *(LoadCommand *)p;
-    p += lc.cmdsize;
-    if (lc.cmd != LC_SEGMENT_64)
-      continue;
+  MachSection *mach_sec = (MachSection *)((u8 *)cmd + sizeof(*cmd));
 
-    SegmentCommand &cmd = *(SegmentCommand *)&lc;
-    MachSection *mach_sec = (MachSection *)((u8 *)&lc + sizeof(cmd));
+  for (MachSection &msec : std::span(mach_sec, mach_sec + cmd->nsects)) {
+    sections.push_back(nullptr);
 
-    for (MachSection &msec : std::span(mach_sec, mach_sec + cmd.nsects)) {
-      sections.push_back(nullptr);
+    if (msec.match("__LD", "__compact_unwind")) {
+      unwind_sec = &msec;
+      continue;
+    }
 
-      if (msec.match("__LD", "__compact_unwind")) {
-        unwind_sec = &msec;
-        continue;
-      }
+    if (msec.match("__DATA", "__objc_imageinfo") ||
+        msec.match("__DATA_CONST", "__objc_imageinfo")) {
+      if (msec.size != sizeof(ObjcImageInfo))
+        Fatal(ctx) << *this << ": __objc_imageinfo: invalid size";
+
+      objc_image_info =
+        (ObjcImageInfo *)(this->mf->get_contents().data() + msec.offset);
+
+      if (objc_image_info->version != 0)
+        Fatal(ctx) << *this << ": __objc_imageinfo: unknown version: "
+                   << (u32)objc_image_info->version;
+      continue;
+    }
 
-      if (msec.attr & S_ATTR_DEBUG)
-        continue;
+    if (msec.attr & S_ATTR_DEBUG)
+      continue;
 
-      sections.back().reset(new InputSection<E>(ctx, *this, msec));
-    }
+    sections.back().reset(new InputSection<E>(ctx, *this, msec));
   }
 }
 
@@ -139,21 +146,9 @@ void ObjectFile<E>::parse_symbols(Contex
       sym.scope = SCOPE_LOCAL;
       sym.is_common = false;
       sym.is_weak = false;
-
-      switch (msym.type) {
-      case N_UNDF:
-        Fatal(ctx) << sym << ": local undef symbol?";
-      case N_ABS:
+      if (msym.type == N_ABS)
         sym.value = msym.value;
-        break;
-      case N_SECT:
-        // `value` and `subsec` will be filled by split_subsections
-        break;
-      default:
-        Fatal(ctx) << *this << ": unknown symbol type for "
-                   << sym << ": " << (u64)msym.type;
-      }
-
+      sym.no_dead_strip = (msym.desc & N_NO_DEAD_STRIP);
       this->syms.push_back(&sym);
     }
   }
@@ -179,13 +174,13 @@ split_regular_sections(Context<E> &ctx,
 
   for (i64 i = 0; i < file.sections.size(); i++)
     if (InputSection<E> *isec = file.sections[i].get())
-      if (!isec->hdr.match("__TEXT", "__cstring"))
+      if (isec->hdr.type != S_CSTRING_LITERALS)
         vec[i].isec = isec;
 
   // Find all symbols whose type is N_SECT.
   for (i64 i = 0; i < file.mach_syms.size(); i++) {
     MachSym &msym = file.mach_syms[i];
-    if (msym.type == N_SECT && vec[msym.sect - 1].isec) {
+    if (!msym.stab && msym.type == N_SECT && vec[msym.sect - 1].isec) {
       SplitRegion r;
       r.offset = msym.value - vec[msym.sect - 1].isec->hdr.addr;
       r.symidx = i;
@@ -200,6 +195,25 @@ split_regular_sections(Context<E> &ctx,
     return a.isec->hdr.addr < b.isec->hdr.addr;
   });
 
+  for (SplitInfo<E> &info : vec) {
+    sort(info.regions, [](const SplitRegion &a, const SplitRegion &b) {
+      return a.offset < b.offset;
+    });
+  }
+
+  // If two symbols point to the same location, we create only one
+  // subsection.
+  for (SplitInfo<E> &info : vec) {
+    i64 last = -1;
+    for (SplitRegion &r : info.regions) {
+      if (!r.is_alt_entry) {
+        if (r.offset == last)
+          r.is_alt_entry = true;
+        last = r.offset;
+      }
+    }
+  }
+
   // Fix regions so that they cover the entire section without overlapping.
   for (SplitInfo<E> &info : vec) {
     std::vector<SplitRegion> &r = info.regions;
@@ -209,10 +223,6 @@ split_regular_sections(Context<E> &ctx,
       continue;
     }
 
-    sort(r, [](const SplitRegion &a, const SplitRegion &b) {
-      return a.offset < b.offset;
-    });
-
     if (r[0].offset > 0)
       r.insert(r.begin(), {0, r[0].offset, (u32)-1, false});
 
@@ -239,13 +249,15 @@ template <typename E>
 void ObjectFile<E>::split_subsections_via_symbols(Context<E> &ctx) {
   sym_to_subsec.resize(mach_syms.size());
 
-  auto add = [&](InputSection<E> &isec, u32 offset, u32 size, u8 p2align) {
+  auto add = [&](InputSection<E> &isec, u32 offset, u32 size, u8 p2align,
+                 bool is_cstring) {
     Subsection<E> *subsec = new Subsection<E>{
       .isec = isec,
       .input_offset = offset,
       .input_size = size,
       .input_addr = (u32)(isec.hdr.addr + offset),
       .p2align = p2align,
+      .is_cstring = is_cstring,
     };
 
     subsec_pool.emplace_back(subsec);
@@ -257,7 +269,7 @@ void ObjectFile<E>::split_subsections_vi
     InputSection<E> &isec = *info.isec;
     for (SplitRegion &r : info.regions) {
       if (!r.is_alt_entry)
-        add(isec, r.offset, r.size, isec.hdr.p2align);
+        add(isec, r.offset, r.size, isec.hdr.p2align, false);
       if (r.symidx != -1)
         sym_to_subsec[r.symidx] = subsections.back();
     }
@@ -265,14 +277,14 @@ void ObjectFile<E>::split_subsections_vi
 
   // Split __cstring section.
   for (std::unique_ptr<InputSection<E>> &isec : sections) {
-    if (isec && isec->hdr.match("__TEXT", "__cstring")) {
+    if (isec && isec->hdr.type == S_CSTRING_LITERALS) {
       std::string_view str = isec->contents;
       size_t pos = 0;
 
       while (pos < str.size()) {
         size_t end = str.find('\0', pos);
         if (end == str.npos)
-          Fatal(ctx) << *this << " corruupted __TEXT,__cstring";
+          Fatal(ctx) << *this << " corruupted cstring section: " << *isec;
 
         end = str.find_first_not_of('\0', end);
         if (end == str.npos)
@@ -281,7 +293,7 @@ void ObjectFile<E>::split_subsections_vi
         // A constant string in __cstring has no alignment info, so we
         // need to infer it.
         u8 p2align = std::min<u8>(isec->hdr.p2align, std::countr_zero(pos));
-        add(*isec, pos, end - pos, p2align);
+        add(*isec, pos, end - pos, p2align, true);
         pos = end;
       }
     }
@@ -310,7 +322,7 @@ void ObjectFile<E>::init_subsections(Con
 
   for (i64 i = 0; i < mach_syms.size(); i++) {
     MachSym &msym = mach_syms[i];
-    if (msym.type == N_SECT)
+    if (!msym.stab && msym.type == N_SECT)
       sym_to_subsec[i] = subsections[msym.sect - 1];
   }
 
@@ -324,7 +336,7 @@ void ObjectFile<E>::fix_subsec_members(C
     MachSym &msym = mach_syms[i];
     Symbol<E> &sym = *this->syms[i];
 
-    if (!msym.is_extern && msym.type == N_SECT) {
+    if (!msym.stab && !msym.is_extern && msym.type == N_SECT) {
       Subsection<E> *subsec = sym_to_subsec[i];
       if (!subsec)
         subsec = find_subsection(ctx, msym.value);
@@ -356,21 +368,31 @@ std::vector<std::string> ObjectFile<E>::
   if (get_file_type(this->mf) == FileType::LLVM_BITCODE)
     return {};
 
-  auto *cmd = (LinkerOptionCommand *)find_load_command(ctx, LC_LINKER_OPTION);
-  if (!cmd)
-    return {};
-
-  char *buf = (char *)cmd + sizeof(*cmd);
+  MachHeader &hdr = *(MachHeader *)this->mf->data;
+  u8 *p = this->mf->data + sizeof(hdr);
   std::vector<std::string> vec;
-  for (i64 i = 0; i < cmd->count; i++) {
-    vec.push_back(buf);
-    buf += vec.back().size() + 1;
+
+  for (i64 i = 0; i < hdr.ncmds; i++) {
+    LoadCommand &lc = *(LoadCommand *)p;
+    p += lc.cmdsize;
+
+    if (lc.cmd == LC_LINKER_OPTION) {
+      LinkerOptionCommand *cmd = (LinkerOptionCommand *)&lc;
+      char *buf = (char *)cmd + sizeof(*cmd);
+      for (i64 i = 0; i < cmd->count; i++) {
+        vec.push_back(buf);
+        buf += vec.back().size() + 1;
+      }
+    }
   }
   return vec;
 }
 
 template <typename E>
 LoadCommand *ObjectFile<E>::find_load_command(Context<E> &ctx, u32 type) {
+  if (!this->mf)
+    return nullptr;
+
   MachHeader &hdr = *(MachHeader *)this->mf->data;
   u8 *p = this->mf->data + sizeof(hdr);
 
@@ -385,14 +407,11 @@ LoadCommand *ObjectFile<E>::find_load_co
 
 template <typename E>
 Subsection<E> *ObjectFile<E>::find_subsection(Context<E> &ctx, u32 addr) {
-  auto it = std::upper_bound(subsections.begin(), subsections.end(), addr,
-                             [&](u32 addr, Subsection<E> *subsec) {
-    return addr < subsec->input_addr;
-  });
-
-  if (it == subsections.begin())
-    return nullptr;
-  return *(it - 1);
+  for (Subsection<E> *subsec : subsections)
+    if (subsec->input_addr <= addr &&
+        addr < subsec->input_addr + subsec->input_size)
+      return subsec;
+  return nullptr;
 }
 
 template <typename E>
@@ -428,7 +447,8 @@ void ObjectFile<E>::parse_compact_unwind
     UnwindRecord<E> &dst = unwind_records[idx];
 
     auto error = [&] {
-      Fatal(ctx) << *this << ": __compact_unwind: unsupported relocation: " << i;
+      Fatal(ctx) << *this << ": __compact_unwind: unsupported relocation: " << i
+                 << " " << *this->syms[r.idx];
     };
 
     if (r.is_pcrel || r.p2size != 3 || r.type)
@@ -436,12 +456,15 @@ void ObjectFile<E>::parse_compact_unwind
 
     switch (r.offset % sizeof(CompactUnwindEntry)) {
     case offsetof(CompactUnwindEntry, code_start): {
+      Subsection<E> *target;
       if (r.is_extern)
-        error();
+        target = sym_to_subsec[r.idx];
+      else
+        target = find_subsection(ctx, src[idx].code_start);
 
-      Subsection<E> *target = find_subsection(ctx, src[idx].code_start);
       if (!target)
         error();
+
       dst.subsec = target;
       dst.offset = src[idx].code_start - target->input_addr;
       break;
@@ -452,19 +475,24 @@ void ObjectFile<E>::parse_compact_unwind
       } else {
         u32 addr = *(ul32 *)((u8 *)this->mf->data + hdr.offset + r.offset);
         dst.personality = find_symbol(ctx, addr);
-        if (!dst.personality)
-          Fatal(ctx) << *this << ": __compact_unwind: unsupported local "
-                     << "personality reference: " << i;
       }
+
+      if (!dst.personality)
+        Fatal(ctx) << *this << ": __compact_unwind: unsupported "
+                   << "personality reference: " << i;
       break;
     case offsetof(CompactUnwindEntry, lsda): {
+      u32 addr = *(ul32 *)((u8 *)this->mf->data + hdr.offset + r.offset);
+
+      Subsection<E> *target;
       if (r.is_extern)
-        error();
+        target = sym_to_subsec[r.idx];
+      else
+        target = find_subsection(ctx, addr);
 
-      u32 addr = *(ul32 *)((u8 *)this->mf->data + hdr.offset + r.offset);
-      Subsection<E> *target = find_subsection(ctx, addr);
       if (!target)
         error();
+
       dst.lsda = target;
       dst.lsda_offset = addr - target->input_addr;
       break;
@@ -567,6 +595,7 @@ void ObjectFile<E>::resolve_symbols(Cont
       sym.file = this;
       sym.is_imported = false;
       sym.is_weak = is_weak;
+      sym.no_dead_strip = (msym.desc & N_NO_DEAD_STRIP);
 
       switch (msym.type) {
       case N_UNDF:
@@ -597,13 +626,15 @@ bool ObjectFile<E>::is_objc_object(Conte
   for (std::unique_ptr<InputSection<E>> &isec : sections)
     if (isec)
       if (isec->hdr.match("__DATA", "__objc_catlist") ||
-          isec->hdr.match("__TEXT", "__swift"))
-      return true;
+          (isec->hdr.get_segname() == "__TEXT" &&
+           isec->hdr.get_sectname().starts_with("__swift")))
+        return true;
 
   for (i64 i = 0; i < this->syms.size(); i++)
     if (!mach_syms[i].is_undef() && mach_syms[i].is_extern &&
         this->syms[i]->name.starts_with("_OBJC_CLASS_$_"))
       return true;
+
   return false;
 }
 
@@ -623,11 +654,18 @@ ObjectFile<E>::mark_live_objects(Context
     if (!sym.file)
       continue;
 
-    bool keep = msym.is_undef() || (msym.is_common() && !sym.is_common);
-    if (keep && !sym.file->is_alive.exchange(true))
-      if (!sym.file->is_dylib)
-        feeder((ObjectFile<E> *)sym.file);
+    if (msym.is_undef() || (msym.is_common() && !sym.is_common))
+      if (InputFile<E> *file = sym.file;
+          !file->is_alive.exchange(true) && !file->is_dylib)
+        feeder((ObjectFile<E> *)file);
   }
+
+  for (Subsection<E> *subsec : subsections)
+    for (UnwindRecord<E> &rec : subsec->get_unwind_records())
+      if (Symbol<E> *sym = rec.personality)
+        if (InputFile<E> *file = sym->file;
+            !file->is_alive.exchange(true) && !file->is_dylib)
+          feeder((ObjectFile<E> *)file);
 }
 
 template <typename E>
@@ -641,13 +679,14 @@ void ObjectFile<E>::convert_common_symbo
       Subsection<E> *subsec = new Subsection<E>{
         .isec = *isec,
         .input_size = (u32)msym.value,
-        .p2align = (u8)msym.p2align,
+        .p2align = (u8)msym.common_p2align,
       };
 
       subsections.emplace_back(subsec);
 
       sym.is_imported = false;
       sym.is_weak = false;
+      sym.no_dead_strip = (msym.desc & N_NO_DEAD_STRIP);
       sym.subsec = subsec;
       sym.value = 0;
       sym.is_common = false;
@@ -695,9 +734,7 @@ void ObjectFile<E>::parse_lto_symbols(Co
     this->syms.push_back(get_symbol(ctx, name));
 
     u32 attr = ctx.lto.module_get_symbol_attribute(this->lto_module, i);
-
     MachSym msym = {};
-    msym.p2align = (attr & LTO_SYMBOL_ALIGNMENT_MASK);
 
     switch (attr & LTO_SYMBOL_DEFINITION_MASK) {
     case LTO_SYMBOL_DEFINITION_REGULAR:
@@ -734,6 +771,16 @@ void ObjectFile<E>::parse_lto_symbols(Co
 }
 
 template <typename E>
+std::string_view ObjectFile<E>::get_linker_optimization_hints(Context<E> &ctx) {
+  LinkEditDataCommand *cmd =
+    (LinkEditDataCommand *)find_load_command(ctx, LC_LINKER_OPTIMIZATION_HINT);
+
+  if (cmd)
+    return {(char *)this->mf->data + cmd->dataoff, cmd->datasize};
+  return {};
+}
+
+template <typename E>
 DylibFile<E>::DylibFile(Context<E> &ctx, MappedFile<Context<E>> *mf)
   : InputFile<E>(mf) {
   this->is_dylib = true;
@@ -788,6 +835,10 @@ void DylibFile<E>::parse(Context<E> &ctx
   case FileType::MACH_DYLIB:
     parse_dylib(ctx);
     break;
+  case FileType::MACH_EXE:
+    parse_dylib(ctx);
+    dylib_idx = BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE;
+    break;
   default:
     Fatal(ctx) << *this << ": is not a dylib";
   }
@@ -828,10 +879,24 @@ void DylibFile<E>::read_trie(Context<E>
     i64 flags = read_uleb(buf);
     read_uleb(buf); // addr
 
-    if (flags == EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION)
-      weak_exports.insert(save_string(ctx, prefix));
-    else
-      exports.insert(save_string(ctx, prefix));
+    std::string_view name = save_string(ctx, prefix);
+    i64 type = flags & ~EXPORT_SYMBOL_FLAGS_KIND_MASK;
+
+    switch (type) {
+    case 0:
+      exports.insert(name);
+      break;
+    case EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION:
+      weak_exports.insert(name);
+      break;
+    case EXPORT_SYMBOL_FLAGS_REEXPORT:
+      read_uleb(buf); // skip a library ordinal
+      exports.insert(name);
+      break;
+    default:
+      Fatal(ctx) << *this << ": " << name << ": unknown export symbol type: 0x"
+                 << std::hex << type;
+    }
   } else {
     buf++;
   }
@@ -885,12 +950,13 @@ void DylibFile<E>::parse_dylib(Context<E
       read_trie(ctx, this->mf->data + cmd.dataoff);
       break;
     }
-    case LC_REEXPORT_DYLIB: {
-      DylibCommand &cmd = *(DylibCommand *)p;
-      reexported_libs.push_back((char *)p + cmd.nameoff);
+    case LC_REEXPORT_DYLIB:
+      if (!(hdr.flags & MH_NO_REEXPORTED_DYLIBS)) {
+        DylibCommand &cmd = *(DylibCommand *)p;
+        reexported_libs.push_back((char *)p + cmd.nameoff);
+      }
       break;
     }
-    }
     p += lc.cmdsize;
   }
 }
@@ -906,6 +972,7 @@ void DylibFile<E>::resolve_symbols(Conte
       sym.scope = SCOPE_LOCAL;
       sym.is_imported = true;
       sym.is_weak = (this->is_weak || is_weak_symbol[i]);
+      sym.no_dead_strip = false;
       sym.subsec = nullptr;
       sym.value = 0;
       sym.is_common = false;
diff -pruN 1.3.1+dfsg-1/macho/input-sections.cc 1.4.0+dfsg-1/macho/input-sections.cc
--- 1.3.1+dfsg-1/macho/input-sections.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/macho/input-sections.cc	2022-08-04 12:47:21.000000000 +0000
@@ -3,11 +3,29 @@
 namespace mold::macho {
 
 template <typename E>
+OutputSection<E> &get_output_section(Context<E> &ctx, const MachSection &hdr) {
+  static std::unordered_set<std::string_view> set = {
+    "__got", "__auth_got", "__auth_ptr", "__nl_symbol_ptr", "__const",
+    "__cfstring", "__mod_init_func", "__mod_term_func", "__objc_classlist",
+    "__objc_nlclslist", "__objc_catlist", "__objc_nlcatlist", "__objc_protolist",
+  };
+
+  std::string_view seg = hdr.get_segname();
+  std::string_view sect = hdr.get_sectname();
+
+  if (seg == "__DATA" && set.contains(sect)) {
+    seg = "__DATA_CONST";
+  } else if (seg == "__TEXT" && sect == "__StaticInit") {
+    sect = "__text";
+  }
+
+  return *OutputSection<E>::get_instance(ctx, seg, sect);
+}
+
+template <typename E>
 InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
                               const MachSection &hdr)
-  : file(file), hdr(hdr),
-    osec(*OutputSection<E>::get_instance(ctx, hdr.get_segname(),
-                                         hdr.get_sectname())) {
+  : file(file), hdr(hdr), osec(get_output_section(ctx, hdr)) {
   if (hdr.type != S_ZEROFILL)
     contents = file.mf->get_contents().substr(hdr.offset, hdr.size);
 }
diff -pruN 1.3.1+dfsg-1/macho/lto.cc 1.4.0+dfsg-1/macho/lto.cc
--- 1.3.1+dfsg-1/macho/lto.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/macho/lto.cc	2022-08-04 12:47:21.000000000 +0000
@@ -75,7 +75,7 @@ static void do_load_plugin(Context<E> &c
 
 template <typename E>
 void load_lto_plugin(Context<E> &ctx) {
-  std:call_once(ctx.lto_plugin_loaded, [&]() { do_load_plugin(ctx); });
+  std::call_once(ctx.lto_plugin_loaded, [&]() { do_load_plugin(ctx); });
 }
 
 template <typename E>
@@ -93,7 +93,7 @@ void do_lto(Context<E> &ctx) {
   // Mark symbols that have to be preserved. All symbols that are not
   // marked here may be internalized and deleted as an extenrally-
   // visible symbol.
-  if (ctx.arg.dylib || ctx.arg.export_dynamic) {
+  if (ctx.output_type == MH_DYLIB || ctx.arg.export_dynamic) {
     for (ObjectFile<E> *file : ctx.objs) {
       if (!file->lto_module) {
         for (i64 i = 0; i < file->mach_syms.size(); i++) {
diff -pruN 1.3.1+dfsg-1/macho/lto.h 1.4.0+dfsg-1/macho/lto.h
--- 1.3.1+dfsg-1/macho/lto.h	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/macho/lto.h	2022-08-04 12:47:21.000000000 +0000
@@ -62,9 +62,9 @@ struct LTOPlugin {
   void *dlopen_handle = nullptr;
   ~LTOPlugin();
 
-  const char (*get_version)();
+  char (*get_version)();
 
-  const char (*get_error_message)();
+  char (*get_error_message)();
 
   bool (*module_is_object_file)(const char *path);
 
diff -pruN 1.3.1+dfsg-1/macho/macho.h 1.4.0+dfsg-1/macho/macho.h
--- 1.3.1+dfsg-1/macho/macho.h	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/macho/macho.h	2022-08-04 12:47:21.000000000 +0000
@@ -20,8 +20,8 @@ typedef int16_t i16;
 typedef int32_t i32;
 typedef int64_t i64;
 
-class ARM64;
-class X86_64;
+struct ARM64;
+struct X86_64;
 
 template <typename E>
 std::string rel_to_string(u8 r_type);
@@ -162,13 +162,13 @@ static constexpr u32 S_THREAD_LOCAL_VARI
 static constexpr u32 S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15;
 static constexpr u32 S_INIT_FUNC_OFFSETS = 0x16;
 
-static constexpr u32 S_ATTR_LOC_RELOC = 0x000001;
-static constexpr u32 S_ATTR_EXT_RELOC = 0x000002;
-static constexpr u32 S_ATTR_SOME_INSTRUCTIONS = 0x000004;
-
-static constexpr u32 S_ATTR_DEBUG = 0x020000;
-static constexpr u32 S_ATTR_SELF_MODIFYING_CODE = 0x040000;
-static constexpr u32 S_ATTR_LIVE_SUPPORT = 0x080000;
+static constexpr u32 S_ATTR_LOC_RELOC = 0x1;
+static constexpr u32 S_ATTR_EXT_RELOC = 0x2;
+static constexpr u32 S_ATTR_SOME_INSTRUCTIONS = 0x4;
+
+static constexpr u32 S_ATTR_DEBUG = 0x20000;
+static constexpr u32 S_ATTR_SELF_MODIFYING_CODE = 0x40000;
+static constexpr u32 S_ATTR_LIVE_SUPPORT = 0x80000;
 static constexpr u32 S_ATTR_NO_DEAD_STRIP = 0x100000;
 static constexpr u32 S_ATTR_STRIP_STATIC_SYMS = 0x200000;
 static constexpr u32 S_ATTR_NO_TOC = 0x400000;
@@ -317,7 +317,23 @@ static constexpr u32 PLATFORM_DRIVERKIT
 static constexpr u32 TOOL_CLANG = 1;
 static constexpr u32 TOOL_SWIFT = 2;
 static constexpr u32 TOOL_LD = 3;
-static constexpr u32 TOOL_MOLD = 0x6d6f6c64; // Hex in "mold"
+static constexpr u32 TOOL_MOLD = 54321; // Randomly chosen!
+
+static constexpr u32 OBJC_IMAGE_SUPPORTS_GC = 1 << 1;
+static constexpr u32 OBJC_IMAGE_REQUIRES_GC = 1 << 2;
+static constexpr u32 OBJC_IMAGE_OPTIMIZED_BY_DYLD = 1 << 3;
+static constexpr u32 OBJC_IMAGE_SUPPORTS_COMPACTION = 1 << 4;
+static constexpr u32 OBJC_IMAGE_IS_SIMULATED = 1 << 5;
+static constexpr u32 OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES = 1 << 6;
+
+static constexpr u32 LOH_ARM64_ADRP_ADRP = 1;
+static constexpr u32 LOH_ARM64_ADRP_LDR = 2;
+static constexpr u32 LOH_ARM64_ADRP_ADD_LDR = 3;
+static constexpr u32 LOH_ARM64_ADRP_LDR_GOT_LDR = 4;
+static constexpr u32 LOH_ARM64_ADRP_ADD_STR = 5;
+static constexpr u32 LOH_ARM64_ADRP_LDR_GOT_STR = 6;
+static constexpr u32 LOH_ARM64_ADRP_ADD = 7;
+static constexpr u32 LOH_ARM64_ADRP_LDR_GOT = 8;
 
 static constexpr u32 ARM64_RELOC_UNSIGNED = 0;
 static constexpr u32 ARM64_RELOC_SUBTRACTOR = 1;
@@ -600,18 +616,27 @@ struct MachSym {
   }
 
   ul32 stroff;
-  u8 is_extern : 1;
-  u8 type : 3;
-  u8 is_private_extern : 1;
-  u8 stub : 3;
+
+  union {
+    u8 n_type;
+    struct {
+      u8 is_extern : 1;
+      u8 type : 3;
+      u8 is_private_extern : 1;
+      u8 stab : 3;
+    };
+  };
+
   u8 sect;
+
   union {
     ul16 desc;
     struct {
       u8 padding;
-      u8 p2align : 4;
+      u8 common_p2align : 4;
     };
   };
+
   ul64 value;
 };
 
@@ -723,9 +748,18 @@ struct CodeSignatureDirectory {
   ub64 exec_seg_flags;
 };
 
+// __DATA,__objc_imageinfo
+struct ObjcImageInfo {
+  ul32 version = 0;
+  u8 flags = 0;
+  u8 swift_version = 0;
+  ul16 swift_lang_version = 0;
+};
+
 struct ARM64 {
   static constexpr u32 cputype = CPU_TYPE_ARM64;
   static constexpr u32 cpusubtype = CPU_SUBTYPE_ARM64_ALL;
+  static constexpr u32 page_size = 16384;
   static constexpr u32 abs_rel = ARM64_RELOC_UNSIGNED;
   static constexpr u32 word_size = 8;
   static constexpr u32 stub_size = 12;
@@ -736,6 +770,7 @@ struct ARM64 {
 struct X86_64 {
   static constexpr u32 cputype = CPU_TYPE_X86_64;
   static constexpr u32 cpusubtype = CPU_SUBTYPE_X86_64_ALL;
+  static constexpr u32 page_size = 4096;
   static constexpr u32 abs_rel = X86_64_RELOC_UNSIGNED;
   static constexpr u32 word_size = 8;
   static constexpr u32 stub_size = 6;
diff -pruN 1.3.1+dfsg-1/macho/main.cc 1.4.0+dfsg-1/macho/main.cc
--- 1.3.1+dfsg-1/macho/main.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/macho/main.cc	2022-08-04 12:47:21.000000000 +0000
@@ -1,15 +1,16 @@
 #include "mold.h"
 #include "../archive-file.h"
-#include "../cmdline.h"
 #include "../output-file.h"
 #include "../sha.h"
 
 #include <cstdlib>
 #include <fcntl.h>
+#include <fstream>
 #include <iomanip>
 #include <iostream>
 #include <sys/mman.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <sys/types.h>
 #include <tbb/concurrent_vector.h>
 #include <tbb/global_control.h>
@@ -114,6 +115,7 @@ static void create_internal_file(Context
   obj->mach_syms = obj->mach_syms2;
   ctx.obj_pool.emplace_back(obj);
   ctx.objs.push_back(obj);
+  ctx.internal_obj = obj;
 
   auto add = [&](std::string_view name) {
     Symbol<E> *sym = get_symbol(ctx, name);
@@ -143,6 +145,27 @@ static void create_internal_file(Context
   }
 
   add("___dso_handle");
+
+  // Add start stop symbols.
+  std::set<std::string_view> start_stop_symbols;
+  std::mutex mu;
+
+  tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
+    std::set<std::string_view> set;
+    for (Symbol<E> *sym : file->syms)
+      if (!sym->file)
+        if (sym->name.starts_with("segment$start$") ||
+            sym->name.starts_with("segment$end$") ||
+            sym->name.starts_with("section$start$") ||
+            sym->name.starts_with("section$end$"))
+          set.insert(sym->name);
+
+    std::scoped_lock lock(mu);
+    start_stop_symbols.merge(set);
+  });
+
+  for (std::string_view name : start_stop_symbols)
+    add(name);
 }
 
 // Remove unreferenced subsections to eliminate code and data
@@ -204,7 +227,6 @@ static bool compare_chunks(const Chunk<E
     // __TEXT
     "__mach_header",
     "__text",
-    "__StaticInit",
     "__stubs",
     "__stub_helper",
     "__gcc_except_tab",
@@ -288,107 +310,6 @@ static void claim_unresolved_symbols(Con
 }
 
 template <typename E>
-static void merge_cstring_sections(Context<E> &ctx) {
-  Timer t(ctx, "merge_cstring_sections");
-
-  struct Entry {
-    Entry(Subsection<E> *subsec) : owner(subsec) {}
-
-    Entry(const Entry &other) :
-      owner(other.owner.load()), p2align(other.p2align.load()) {}
-
-    std::atomic<Subsection<E> *> owner = nullptr;
-    std::atomic_uint8_t p2align = 0;
-  };
-
-  struct SubsecRef {
-    Subsection<E> &subsec;
-    u64 hash = 0;
-    Entry *ent = nullptr;
-  };
-
-  std::vector<std::vector<SubsecRef>> vec(ctx.objs.size());
-
-  // Estimate the number of unique strings.
-  HyperLogLog estimator;
-
-  tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
-    ObjectFile<E> *file = ctx.objs[i];
-    HyperLogLog e;
-
-    for (Subsection<E> *subsec : file->subsections) {
-      if (&subsec->isec.osec == ctx.cstring) {
-        std::string_view str = subsec->get_contents();
-        u64 h = hash_string(str);
-        vec[i].push_back({*subsec, h, nullptr});
-        estimator.insert(h);
-      }
-    }
-    estimator.merge(e);
-  });
-
-  // Create a hash map large enough to hold all strings.
-  ConcurrentMap<Entry> map(estimator.get_cardinality() * 3 / 2);
-
-  // Insert all strings into the hash table.
-  tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
-    ObjectFile<E> *file = ctx.objs[i];
-
-    for (i64 j = 0; j < vec[i].size(); j++) {
-      SubsecRef &ref = vec[i][j];
-      std::string_view s = ref.subsec.get_contents();
-      ref.ent = map.insert(s, ref.hash, {&ref.subsec}).first;
-
-      Subsection<E> *existing = ref.ent->owner;
-      while (existing->isec.file.priority < file->priority &&
-             !ref.ent->owner.compare_exchange_weak(existing, &ref.subsec));
-
-      update_maximum(ref.ent->p2align, ref.subsec.p2align.load());
-    }
-  });
-
-  // Decide who will become the owner for each subsection.
-  tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
-    ObjectFile<E> *file = ctx.objs[i];
-
-    for (i64 j = 0; j < vec[i].size(); j++) {
-      SubsecRef &ref = vec[i][j];
-      if (ref.ent->owner != &ref.subsec) {
-        ref.subsec.is_coalesced = true;
-        ref.subsec.replacer = ref.ent->owner;
-
-        static Counter counter("num_merged_strings");
-        counter++;
-      }
-    }
-  });
-
-  // Merge strings
-  tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
-    for (std::unique_ptr<InputSection<E>> &isec : file->sections)
-      if (isec)
-        for (Relocation<E> &r : isec->rels)
-          if (r.subsec && r.subsec->is_coalesced)
-            r.subsec = r.subsec->replacer;
-  });
-
-  auto replace = [&](InputFile<E> *file) {
-    for (Symbol<E> *sym : file->syms)
-      if (sym->subsec && sym->subsec->is_coalesced)
-        sym->subsec = sym->subsec->replacer;
-  };
-
-  tbb::parallel_for_each(ctx.objs, replace);
-  tbb::parallel_for_each(ctx.dylibs, replace);
-
-  tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
-    std::erase_if(file->subsections, [](Subsection<E> *subsec) {
-      return subsec->is_coalesced;
-    });
-  });
-}
-
-template <typename E>
 static Chunk<E> *find_section(Context<E> &ctx, std::string_view segname,
                               std::string_view sectname) {
   for (Chunk<E> *chunk : ctx.chunks)
@@ -401,6 +322,13 @@ template <typename E>
 static void create_synthetic_chunks(Context<E> &ctx) {
   Timer t(ctx, "create_synthetic_chunks");
 
+  // Create a __DATA,__objc_imageinfo section.
+  ctx.image_info = ObjcImageInfoSection<E>::create(ctx);
+
+  // Create a __LINKEDIT,__func_starts section.
+  if (ctx.arg.function_starts)
+    ctx.function_starts.reset(new FunctionStartsSection(ctx));
+
   // Handle -sectcreate
   for (SectCreateOption arg : ctx.arg.sectcreate) {
     MappedFile<Context<E>> *mf =
@@ -449,6 +377,109 @@ static void create_synthetic_chunks(Cont
 }
 
 template <typename E>
+static void uniquify_cstrings(Context<E> &ctx, OutputSection<E> &osec) {
+  Timer t(ctx, "uniquify_cstrings");
+
+  struct Entry {
+    Entry(Subsection<E> *subsec) : owner(subsec) {}
+
+    Entry(const Entry &other) :
+      owner(other.owner.load()), p2align(other.p2align.load()) {}
+
+    std::atomic<Subsection<E> *> owner = nullptr;
+    std::atomic_uint8_t p2align = 0;
+  };
+
+  struct SubsecRef {
+    Subsection<E> *subsec = nullptr;
+    u64 hash = 0;
+    Entry *ent = nullptr;
+  };
+
+  std::vector<SubsecRef> vec(osec.members.size());
+
+  // Estimate the number of unique strings.
+  tbb::enumerable_thread_specific<HyperLogLog> estimators;
+
+  tbb::parallel_for((i64)0, (i64)osec.members.size(), [&](i64 i) {
+    Subsection<E> *subsec = osec.members[i];
+    if (subsec->is_cstring) {
+      u64 h = hash_string(subsec->get_contents());
+      vec[i].subsec = subsec;
+      vec[i].hash = h;
+      estimators.local().insert(h);
+    }
+  });
+
+  HyperLogLog estimator;
+  for (HyperLogLog &e : estimators)
+    estimator.merge(e);
+
+  // Create a hash map large enough to hold all strings.
+  ConcurrentMap<Entry> map(estimator.get_cardinality() * 3 / 2);
+
+  // Insert all strings into the hash table.
+  tbb::parallel_for_each(vec, [&](SubsecRef &ref) {
+    if (ref.subsec) {
+      std::string_view s = ref.subsec->get_contents();
+      ref.ent = map.insert(s, ref.hash, {ref.subsec}).first;
+
+      Subsection<E> *existing = ref.ent->owner;
+      while (existing->isec.file.priority < ref.subsec->isec.file.priority &&
+             !ref.ent->owner.compare_exchange_weak(existing, ref.subsec));
+
+      update_maximum(ref.ent->p2align, ref.subsec->p2align.load());
+    }
+  });
+
+  // Decide who will become the owner for each subsection.
+  tbb::parallel_for_each(vec, [&](SubsecRef &ref) {
+    if (ref.subsec && ref.subsec != ref.ent->owner) {
+      ref.subsec->is_coalesced = true;
+      ref.subsec->replacer = ref.ent->owner;
+    }
+  });
+
+  static Counter counter("num_merged_strings");
+  counter += std::erase_if(osec.members, [](Subsection<E> *subsec) {
+    return subsec->is_coalesced;
+  });
+}
+
+template <typename E>
+static void merge_cstring_sections(Context<E> &ctx) {
+  Timer t(ctx, "merge_cstring_sections");
+
+  for (Chunk<E> *chunk : ctx.chunks)
+    if (chunk->is_output_section && chunk->hdr.type == S_CSTRING_LITERALS)
+      uniquify_cstrings(ctx, *(OutputSection<E> *)chunk);
+
+  // Rewrite relocations and symbols.
+  tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
+    for (std::unique_ptr<InputSection<E>> &isec : file->sections)
+      if (isec)
+        for (Relocation<E> &r : isec->rels)
+          if (r.subsec && r.subsec->is_coalesced)
+            r.subsec = r.subsec->replacer;
+  });
+
+  std::vector<InputFile<E> *> files;
+  append(files, ctx.objs);
+  append(files, ctx.dylibs);
+
+  for (InputFile<E> *file: files)
+    for (Symbol<E> *sym : file->syms)
+      if (sym && sym->subsec && sym->subsec->is_coalesced)
+        sym->subsec = sym->subsec->replacer;
+
+  tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
+    std::erase_if(file->subsections, [](Subsection<E> *subsec) {
+      return subsec->is_coalesced;
+    });
+  });
+}
+
+template <typename E>
 static void scan_unwind_info(Context<E> &ctx) {
   tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
     for (Subsection<E> *subsec : file->subsections)
@@ -529,6 +560,59 @@ static void fix_synthetic_symbol_values(
   get_symbol(ctx, "__mh_dylib_header")->value = ctx.data->hdr.addr;
   get_symbol(ctx, "__mh_bundle_header")->value = ctx.data->hdr.addr;
   get_symbol(ctx, "___dso_handle")->value = ctx.data->hdr.addr;
+
+  auto find_segment = [&](std::string_view name) -> SegmentCommand * {
+    for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments)
+      if (seg->cmd.get_segname() == name)
+        return &seg->cmd;
+    return nullptr;
+  };
+
+  auto find_section = [&](std::string_view name) -> MachSection * {
+    size_t pos = name.find('$');
+    if (pos == name.npos)
+      return nullptr;
+
+    std::string_view segname = name.substr(0, pos);
+    std::string_view sectname = name.substr(pos + 1);
+
+    for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments)
+      for (Chunk<E> *chunk : seg->chunks)
+        if (chunk->hdr.match(segname, sectname))
+          return &chunk->hdr;
+    return nullptr;
+  };
+
+  for (Symbol<E> *sym : ctx.internal_obj->syms) {
+    std::string_view name = sym->name;
+
+    if (remove_prefix(name, "segment$start$")) {
+      sym->value = ctx.text->hdr.addr;
+      if (SegmentCommand *cmd = find_segment(name))
+        sym->value = cmd->vmaddr;
+      continue;
+    }
+
+    if (remove_prefix(name, "segment$end$")) {
+      sym->value = ctx.text->hdr.addr;
+      if (SegmentCommand *cmd = find_segment(name))
+        sym->value = cmd->vmaddr + cmd->vmsize;
+      continue;
+    }
+
+    if (remove_prefix(name, "section$start$")) {
+      sym->value = ctx.text->hdr.addr;
+      if (MachSection *hdr = find_section(name))
+        sym->value = hdr->addr;
+      continue;
+    }
+
+    if (remove_prefix(name, "section$end$")) {
+      sym->value = ctx.text->hdr.addr;
+      if (MachSection *hdr = find_section(name))
+        sym->value = hdr->addr + hdr->size;
+    }
+  }
 }
 
 template <typename E>
@@ -579,50 +663,6 @@ static void compute_uuid(Context<E> &ctx
 }
 
 template <typename E>
-MappedFile<Context<E>> *find_framework(Context<E> &ctx, std::string name) {
-  std::string suffix;
-  std::tie(name, suffix) = split_string(name, ',');
-
-  for (std::string path : ctx.arg.framework_paths) {
-    path = get_realpath(path + "/" + name + ".framework/" + name);
-
-    if (!suffix.empty())
-      if (auto *mf = MappedFile<Context<E>>::open(ctx, path + suffix))
-        return mf;
-
-    if (auto *mf = MappedFile<Context<E>>::open(ctx, path + ".tbd"))
-      return mf;
-
-    if (auto *mf = MappedFile<Context<E>>::open(ctx, path))
-      return mf;
-  }
-  Fatal(ctx) << "-framework not found: " << name;
-}
-
-template <typename E>
-MappedFile<Context<E>> *find_library(Context<E> &ctx, std::string name) {
-  auto search = [&](std::vector<std::string> extn) -> MappedFile<Context<E>> * {
-    for (std::string dir : ctx.arg.library_paths) {
-      for (std::string e : extn) {
-        std::string path = dir + "/lib" + name + e;
-        if (MappedFile<Context<E>> *mf = MappedFile<Context<E>>::open(ctx, path))
-          return mf;
-      }
-    }
-    return nullptr;
-  };
-
-  // -search_paths_first
-  if (ctx.arg.search_paths_first)
-    return search({".tbd", ".dylib", ".a"});
-
-  // -search_dylibs_first
-  if (MappedFile<Context<E>> *mf = search({".tbd", ".dylib"}))
-    return mf;
-  return search({".a"});
-}
-
-template <typename E>
 static MappedFile<Context<E>> *
 strip_universal_header(Context<E> &ctx, MappedFile<Context<E>> *mf) {
   FatHeader &hdr = *(FatHeader *)mf->data;
@@ -643,6 +683,7 @@ static void read_file(Context<E> &ctx, M
   switch (get_file_type(mf)) {
   case FileType::TAPI:
   case FileType::MACH_DYLIB:
+  case FileType::MACH_EXE:
     ctx.dylibs.push_back(DylibFile<E>::create(ctx, mf));
     break;
   case FileType::MACH_OBJ:
@@ -650,12 +691,6 @@ static void read_file(Context<E> &ctx, M
     ctx.objs.push_back(ObjectFile<E>::create(ctx, mf, ""));
     break;
   case FileType::AR:
-    if (!ctx.all_load && !ctx.loaded_archives.insert(mf->name).second) {
-      // If the same .a file is specified more than once, ignore all
-      // but the first one because they would be ignored anyway.
-      break;
-    }
-
     for (MappedFile<Context<E>> *child : read_archive_members(ctx, mf))
       if (get_file_type(child) == FileType::MACH_OBJ)
         ctx.objs.push_back(ObjectFile<E>::create(ctx, child, mf->name));
@@ -704,11 +739,71 @@ template <typename E>
 static void read_input_files(Context<E> &ctx, std::span<std::string> args) {
   Timer t(ctx, "read_input_files");
 
-  auto must_find_library = [&](std::string arg) {
-    MappedFile<Context<E>> *mf = find_library(ctx, arg);
+  std::unordered_set<std::string> libs;
+  std::unordered_set<std::string> frameworks;
+
+  auto search = [&](std::vector<std::string> names)
+      -> MappedFile<Context<E>> * {
+    for (std::string dir : ctx.arg.library_paths) {
+      for (std::string name : names) {
+        std::string path = dir + "/lib" + name;
+        if (MappedFile<Context<E>> *mf = MappedFile<Context<E>>::open(ctx, path))
+          return mf;
+        ctx.missing_files.insert(path);
+      }
+    }
+    return nullptr;
+  };
+
+  auto find_library = [&](std::string name) -> MappedFile<Context<E>> * {
+    // -search_paths_first
+    if (ctx.arg.search_paths_first)
+      return search({name + ".tbd", name + ".dylib", name + ".a"});
+
+    // -search_dylibs_first
+    if (MappedFile<Context<E>> *mf = search({name + ".tbd", name + ".dylib"}))
+      return mf;
+    return search({name + ".a"});
+  };
+
+  auto read_library = [&](std::string name) {
+    if (!libs.insert(name).second)
+      return;
+
+    MappedFile<Context<E>> *mf = find_library(name);
+    if (!mf)
+      Fatal(ctx) << "library not found: -l" << name;
+    read_file(ctx, mf);
+  };
+
+  auto find_framework = [&](std::string name) -> MappedFile<Context<E>> * {
+    std::string suffix;
+    std::tie(name, suffix) = split_string(name, ',');
+
+    for (std::string path : ctx.arg.framework_paths) {
+      path = get_realpath(path + "/" + name + ".framework/" + name);
+
+      if (!suffix.empty())
+        if (auto *mf = MappedFile<Context<E>>::open(ctx, path + suffix))
+          return mf;
+
+      if (auto *mf = MappedFile<Context<E>>::open(ctx, path + ".tbd"))
+        return mf;
+
+      if (auto *mf = MappedFile<Context<E>>::open(ctx, path))
+        return mf;
+    }
+    return nullptr;
+  };
+
+  auto read_framework = [&](std::string name) {
+    if (!frameworks.insert(name).second)
+      return;
+
+    MappedFile<Context<E>> *mf = find_framework(name);
     if (!mf)
-      Fatal(ctx) << "library not found: -l" << arg;
-    return mf;
+      Fatal(ctx) << "-framework not found: " << name;
+    read_file(ctx, mf);
   };
 
   while (!args.empty()) {
@@ -749,27 +844,27 @@ static void read_input_files(Context<E>
       read_file(ctx, MappedFile<Context<E>>::must_open(ctx, arg));
       ctx.all_load = orig;
     } else if (opt == "-framework") {
-      read_file(ctx, find_framework(ctx, arg));
+      read_framework(arg);
     } else if (opt == "-needed_framework") {
       ctx.needed_l = true;
-      read_file(ctx, find_framework(ctx, arg));
+      read_framework(arg);
     } else if (opt == "-weak_framework") {
       ctx.weak_l = true;
-      read_file(ctx, find_framework(ctx, arg));
+      read_framework(arg);
     } else if (opt == "-l") {
-      read_file(ctx, must_find_library(arg));
+      read_library(arg);
     } else if (opt == "-needed-l") {
       ctx.needed_l = true;
-      read_file(ctx, must_find_library(arg));
+      read_library(arg);
     } else if (opt == "-hidden-l") {
       ctx.hidden_l = true;
-      read_file(ctx, must_find_library(arg));
+      read_library(arg);
     } else if (opt == "-weak-l") {
       ctx.weak_l = true;
-      read_file(ctx, must_find_library(arg));
+      read_library(arg);
     } else if (opt == "-reexport-l") {
       ctx.reexport_l = true;
-      read_file(ctx, must_find_library(arg));
+      read_library(arg);
     } else {
       unreachable();
     }
@@ -780,17 +875,25 @@ static void read_input_files(Context<E>
     ctx.reexport_l = false;
   }
 
+  // With -bundle_loader, we can import symbols from a main executable.
+  if (!ctx.arg.bundle_loader.empty())
+    read_library(ctx.arg.bundle_loader);
+
   // An object file can contain linker directives to load other object
   // files or libraries, so process them if any.
-  for (ObjectFile<E> *file : ctx.objs) {
+  for (i64 i = 0; i < ctx.objs.size(); i++) {
+    ObjectFile<E> *file = ctx.objs[i];
     std::vector<std::string> opts = file->get_linker_options(ctx);
 
     for (i64 j = 0; j < opts.size();) {
       if (opts[j] == "-framework") {
-        read_file(ctx, find_framework(ctx, opts[j + 1]));
+        read_framework(opts[j + 1]);
         j += 2;
       } else if (opts[j].starts_with("-l")) {
-        read_file(ctx, must_find_library(opts[j].substr(2)));
+        std::string name = opts[j].substr(2);
+        if (libs.insert(name).second)
+          if (MappedFile<Context<E>> *mf = find_library(name))
+            read_file(ctx, mf);
         j++;
       } else {
         Fatal(ctx) << *file << ": unknown LC_LINKER_OPTION command: " << opts[j];
@@ -806,8 +909,9 @@ static void read_input_files(Context<E>
   for (DylibFile<E> *dylib : ctx.dylibs)
     dylib->priority = ctx.file_priority++;
 
-  for (i64 i = 0; i < ctx.dylibs.size(); i++)
-    ctx.dylibs[i]->dylib_idx = i + 1;
+  for (i64 i = 1; DylibFile<E> *file : ctx.dylibs)
+    if (file->dylib_idx != BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
+      file->dylib_idx = i++;
 }
 
 template <typename E>
@@ -820,6 +924,36 @@ static void parse_object_files(Context<E
 }
 
 template <typename E>
+static void write_dependency_info(Context<E> &ctx) {
+  static constexpr u8 LINKER_VERSION = 0;
+  static constexpr u8 INPUT_FILE = 0x10;
+  static constexpr u8 NOT_FOUND_FILE = 0x11;
+  static constexpr u8 OUTPUT_FILE = 0x40;
+
+  std::ofstream out;
+  out.open(std::string(ctx.arg.dependency_info).c_str());
+  if (!out.is_open())
+    Fatal(ctx) << "cannot open " << ctx.arg.dependency_info
+               << ": " << errno_string();
+
+  out << LINKER_VERSION << mold_version << '\0';
+
+  std::set<std::string_view> input_files;
+  for (std::unique_ptr<MappedFile<Context<E>>> &mf : ctx.mf_pool)
+    if (!mf->parent)
+      input_files.insert(mf->name);
+
+  for (std::string_view s : input_files)
+    out << INPUT_FILE << s << '\0';
+
+  for (std::string_view s : ctx.missing_files)
+    out << NOT_FOUND_FILE << s << '\0';
+
+  out << OUTPUT_FILE << ctx.arg.output << '\0';
+  out.close();
+}
+
+template <typename E>
 static void print_stats(Context<E> &ctx) {
   for (ObjectFile<E> *file : ctx.objs) {
     static Counter subsections("num_subsections");
@@ -900,12 +1034,11 @@ static int do_main(int argc, char **argv
 
   claim_unresolved_symbols(ctx);
 
-  merge_cstring_sections(ctx);
-
   if (ctx.arg.dead_strip)
     dead_strip(ctx);
 
   create_synthetic_chunks(ctx);
+  merge_cstring_sections(ctx);
 
   for (ObjectFile<E> *file : ctx.objs)
     file->check_duplicate_symbols(ctx);
@@ -935,12 +1068,20 @@ static int do_main(int argc, char **argv
 
   copy_sections_to_output_file(ctx);
 
+  if constexpr (std::is_same_v<E, ARM64>)
+    if (!ctx.arg.ignore_optimization_hints)
+      apply_linker_optimization_hints(ctx);
+
   if (ctx.code_sig)
     ctx.code_sig->write_signature(ctx);
   else if (ctx.arg.uuid == UUID_HASH)
     compute_uuid(ctx);
 
   ctx.output_file->close(ctx);
+
+  if (!ctx.arg.dependency_info.empty())
+    write_dependency_info(ctx);
+
   ctx.checkpoint();
   t.stop();
 
diff -pruN 1.3.1+dfsg-1/macho/mold.h 1.4.0+dfsg-1/macho/mold.h
--- 1.3.1+dfsg-1/macho/mold.h	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/macho/mold.h	2022-08-04 12:47:21.000000000 +0000
@@ -48,6 +48,7 @@ struct Relocation {
   u8 type = -1;
   u8 p2size = 0;
   bool is_pcrel : 1 = false;
+  bool is_subtracted : 1 = false;
   bool needs_dynrel : 1 = false;
   i64 addend = 0;
   Symbol<E> *sym = nullptr;
@@ -123,6 +124,7 @@ public:
                          std::function<void(ObjectFile<E> *)> feeder);
   void convert_common_symbols(Context<E> &ctx);
   void check_duplicate_symbols(Context<E> &ctx);
+  std::string_view get_linker_optimization_hints(Context<E> &ctx);
 
   Relocation<E> read_reloc(Context<E> &ctx, const MachSection &hdr, MachRel r);
 
@@ -133,6 +135,7 @@ public:
   std::vector<Symbol<E>> local_syms;
   std::vector<UnwindRecord<E>> unwind_records;
   std::span<DataInCodeEntry> data_in_code_entries;
+  ObjcImageInfo *objc_image_info = nullptr;
   LTOModule *lto_module = nullptr;
 
   // For the internal file and LTO object files
@@ -242,6 +245,7 @@ public:
 
   std::atomic_uint8_t p2align = 0;
   std::atomic_bool is_alive = true;
+  bool is_cstring : 1 = false;
   bool is_coalesced : 1 = false;
   bool added_to_osec : 1 = false;
 };
@@ -287,9 +291,10 @@ struct Symbol {
   std::atomic_uint8_t flags = 0;
 
   u8 scope : 2 = SCOPE_LOCAL;
+  bool is_imported : 1 = false;
   bool is_common : 1 = false;
   bool is_weak : 1 = false;
-  bool is_imported : 1 = false;
+  bool no_dead_strip : 1 = false;
   bool referenced_dynamically : 1 = false;
 
   // For range extension thunks
@@ -588,18 +593,34 @@ public:
 };
 
 template <typename E>
+class ObjcImageInfoSection : public Chunk<E> {
+public:
+  static std::unique_ptr<ObjcImageInfoSection> create(Context<E> &ctx);
+
+  ObjcImageInfoSection(Context<E> &ctx, ObjcImageInfo contents)
+    : Chunk<E>(ctx, "__DATA", "__objc_imageinfo"),
+      contents(contents) {
+    this->hdr.p2align = 2;
+    this->hdr.size = sizeof(contents);
+  }
+
+  void copy_buf(Context<E> &ctx) override;
+
+private:
+  ObjcImageInfo contents;
+};
+
+template <typename E>
 class CodeSignatureSection : public Chunk<E> {
 public:
   CodeSignatureSection(Context<E> &ctx)
     : Chunk<E>(ctx, "__LINKEDIT", "__code_signature") {
     this->is_hidden = true;
-    this->hdr.p2align = std::countr_zero(16U);
+    this->hdr.p2align = 3;
   }
 
   void compute_size(Context<E> &ctx) override;
   void write_signature(Context<E> &ctx);
-
-  static constexpr i64 BLOCK_SIZE = 4096;
 };
 
 template <typename E>
@@ -608,7 +629,7 @@ public:
   DataInCodeSection(Context<E> &ctx)
     : Chunk<E>(ctx, "__LINKEDIT", "__data_in_code") {
     this->is_hidden = true;
-    this->hdr.p2align = std::countr_zero(alignof(DataInCodeEntry));
+    this->hdr.p2align = 3;
   }
 
   void compute_size(Context<E> &ctx) override;
@@ -621,7 +642,7 @@ template <typename E>
 class StubsSection : public Chunk<E> {
 public:
   StubsSection(Context<E> &ctx) : Chunk<E>(ctx, "__TEXT", "__stubs") {
-    this->hdr.p2align = std::countr_zero(2U);
+    this->hdr.p2align = 4;
     this->hdr.type = S_SYMBOL_STUBS;
     this->hdr.attr = S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS;
     this->hdr.reserved1 = 0;
@@ -640,7 +661,7 @@ class StubHelperSection : public Chunk<E
 public:
   StubHelperSection(Context<E> &ctx)
     : Chunk<E>(ctx, "__TEXT", "__stub_helper") {
-    this->hdr.p2align = std::countr_zero(4U);
+    this->hdr.p2align = 4;
     this->hdr.attr = S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS;
   }
 
@@ -652,7 +673,7 @@ class UnwindInfoSection : public Chunk<E
 public:
   UnwindInfoSection(Context<E> &ctx)
     : Chunk<E>(ctx, "__TEXT", "__unwind_info") {
-    this->hdr.p2align = std::countr_zero(4U);
+    this->hdr.p2align = 2;
   }
 
   void compute_size(Context<E> &ctx) override;
@@ -785,6 +806,7 @@ void do_lto(Context<E> &ctx);
 //
 
 void create_range_extension_thunks(Context<ARM64> &ctx, OutputSection<ARM64> &osec);
+void apply_linker_optimization_hints(Context<ARM64> &ctx);
 
 //
 // main.cc
@@ -820,11 +842,9 @@ struct Context {
     text = OutputSection<E>::get_instance(*this, "__TEXT", "__text");
     data = OutputSection<E>::get_instance(*this, "__DATA", "__data");
     bss = OutputSection<E>::get_instance(*this, "__DATA", "__bss");
-    cstring = OutputSection<E>::get_instance(*this, "__TEXT", "__cstring");
     common = OutputSection<E>::get_instance(*this, "__DATA", "__common");
 
     bss->hdr.type = S_ZEROFILL;
-    cstring->hdr.type = S_CSTRING_LITERALS;
     common->hdr.type = S_ZEROFILL;
   }
 
@@ -851,10 +871,12 @@ struct Context {
     bool dead_strip_dylibs = false;
     bool deduplicate = true;
     bool demangle = true;
-    bool dylib = false;
     bool dynamic = true;
     bool export_dynamic = false;
     bool fatal_warnings = false;
+    bool function_starts = true;
+    bool ignore_optimization_hints = false;
+    bool mark_dead_strippable_dylib = false;
     bool noinhibit_exec = false;
     bool perf = false;
     bool quick_exit = true;
@@ -872,7 +894,9 @@ struct Context {
     i64 platform_sdk_version = 0;
     i64 stack_size = 0;
     i64 thread_count = 0;
+    std::string bundle_loader;
     std::string chroot;
+    std::string dependency_info;
     std::string final_output;
     std::string install_name;
     std::string lto_library;
@@ -883,6 +907,7 @@ struct Context {
     std::vector<SectAlignOption> sectalign;
     std::vector<SectCreateOption> sectcreate;
     std::vector<std::string> U;
+    std::vector<std::string> add_ast_path;
     std::vector<std::string> framework_paths;
     std::vector<std::string> library_paths;
     std::vector<std::string> mllvm;
@@ -900,7 +925,7 @@ struct Context {
   bool hidden_l = false;
   bool weak_l = false;
   bool reexport_l = false;
-  std::unordered_set<std::string_view> loaded_archives;
+  std::set<std::string> missing_files; // for -dependency_info
 
   u8 uuid[16] = {};
   bool has_error = false;
@@ -925,6 +950,7 @@ struct Context {
 
   std::vector<ObjectFile<E> *> objs;
   std::vector<DylibFile<E> *> dylibs;
+  ObjectFile<E> *internal_obj = nullptr;
 
   OutputSegment<E> *text_seg = nullptr;
   OutputSegment<E> *data_const_seg = nullptr;
@@ -946,16 +972,16 @@ struct Context {
   BindSection<E> bind{*this};
   LazyBindSection<E> lazy_bind{*this};
   ExportSection<E> export_{*this};
-  FunctionStartsSection<E> function_starts{*this};
   SymtabSection<E> symtab{*this};
   StrtabSection<E> strtab{*this};
 
+  std::unique_ptr<FunctionStartsSection<E>> function_starts;
+  std::unique_ptr<ObjcImageInfoSection<E>> image_info;
   std::unique_ptr<CodeSignatureSection<E>> code_sig;
 
   OutputSection<E> *text = nullptr;
   OutputSection<E> *data = nullptr;
   OutputSection<E> *bss = nullptr;
-  OutputSection<E> *cstring = nullptr;
   OutputSection<E> *common = nullptr;
 };
 
diff -pruN 1.3.1+dfsg-1/macho/output-chunks.cc 1.4.0+dfsg-1/macho/output-chunks.cc
--- 1.3.1+dfsg-1/macho/output-chunks.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/macho/output-chunks.cc	2022-08-04 12:47:21.000000000 +0000
@@ -191,8 +191,8 @@ static std::vector<u8> create_function_s
 
   cmd.cmd = LC_FUNCTION_STARTS;
   cmd.cmdsize = buf.size();
-  cmd.dataoff = ctx.function_starts.hdr.offset;
-  cmd.datasize = ctx.function_starts.hdr.size;
+  cmd.dataoff = ctx.function_starts->hdr.offset;
+  cmd.datasize = ctx.function_starts->hdr.size;
   return buf;
 }
 
@@ -275,10 +275,12 @@ static std::vector<std::vector<u8>> crea
     vec.push_back(create_uuid_cmd(ctx));
   vec.push_back(create_build_version_cmd(ctx));
   vec.push_back(create_source_version_cmd(ctx));
-  vec.push_back(create_function_starts_cmd(ctx));
+  if (ctx.arg.function_starts)
+    vec.push_back(create_function_starts_cmd(ctx));
 
-  for (DylibFile<E> *dylib : ctx.dylibs)
-    vec.push_back(create_load_dylib_cmd(ctx, *dylib));
+  for (DylibFile<E> *file : ctx.dylibs)
+    if (file->dylib_idx != BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
+      vec.push_back(create_load_dylib_cmd(ctx, *file));
 
   for (std::string_view rpath : ctx.arg.rpath)
     vec.push_back(create_rpath_cmd(ctx, rpath));
@@ -349,6 +351,9 @@ void OutputMachHeader<E>::copy_buf(Conte
   if (ctx.output_type == MH_DYLIB && !has_reexported_lib(ctx))
     mhdr.flags |= MH_NO_REEXPORTED_DYLIBS;
 
+  if (ctx.arg.mark_dead_strippable_dylib)
+    mhdr.flags |= MH_DEAD_STRIPPABLE_DYLIB;
+
   write_vector(buf + sizeof(mhdr), flatten(cmds));
 }
 
@@ -666,7 +671,8 @@ void RebaseSection<E>::compute_size(Cont
       if (chunk->is_output_section && !chunk->hdr.match("__TEXT", "__eh_frame"))
         for (Subsection<E> *subsec : ((OutputSection<E> *)chunk)->members)
           for (Relocation<E> &rel : subsec->get_rels())
-            if (!rel.is_pcrel && rel.type == E::abs_rel && !refers_tls(rel.sym))
+            if (!rel.is_pcrel && !rel.is_subtracted && rel.type == E::abs_rel &&
+                !refers_tls(rel.sym))
               enc.add(seg->seg_idx,
                       subsec->get_addr(ctx) + rel.offset - seg->cmd.vmaddr);
 
@@ -944,7 +950,7 @@ template <typename E>
 void ExportSection<E>::compute_size(Context<E> &ctx) {
   for (ObjectFile<E> *file : ctx.objs)
     for (Symbol<E> *sym : file->syms)
-      if (sym && sym->file == file & sym->scope == SCOPE_EXTERN)
+      if (sym && sym->file == file && sym->scope == SCOPE_EXTERN)
         enc.entries.push_back({
             sym->name,
             sym->is_weak ? EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION : 0,
@@ -966,6 +972,9 @@ void ExportSection<E>::copy_buf(Context<
   enc.write_trie(buf, enc.root);
 }
 
+// LC_FUNCTION_STARTS contains function start addresses encoded in
+// ULEB128. I don't know what tools consume this table, but we create
+// it anyway by default for the sake of compatibility.
 template <typename E>
 void FunctionStartsSection<E>::compute_size(Context<E> &ctx) {
   std::vector<std::vector<u64>> vec(ctx.objs.size());
@@ -1003,15 +1012,23 @@ void FunctionStartsSection<E>::copy_buf(
 template <typename E>
 void SymtabSection<E>::compute_size(Context<E> &ctx) {
   symtab_offsets.clear();
-  strtab_offsets.clear();
-
   symtab_offsets.resize(ctx.objs.size() + ctx.dylibs.size() + 1);
+
+  strtab_offsets.clear();
   strtab_offsets.resize(ctx.objs.size() + ctx.dylibs.size() + 1);
+  strtab_offsets[0] = 1;
 
   tbb::enumerable_thread_specific<i64> locals;
   tbb::enumerable_thread_specific<i64> globals;
   tbb::enumerable_thread_specific<i64> undefs;
 
+  // Calculate the sizes for -add_ast_path symbols.
+  locals.local() += ctx.arg.add_ast_path.size();
+  symtab_offsets[0] += ctx.arg.add_ast_path.size();
+  for (std::string_view s : ctx.arg.add_ast_path)
+    strtab_offsets[0] += s.size() + 1;
+
+  // Calculate the sizes required for symbols in input files.
   auto count = [&](Symbol<E> *sym) {
     if (sym->is_imported)
       undefs.local() += 1;
@@ -1051,7 +1068,6 @@ void SymtabSection<E>::compute_size(Cont
   for (i64 i = 1; i < symtab_offsets.size(); i++)
     symtab_offsets[i] += symtab_offsets[i - 1];
 
-  strtab_offsets[0] = 1;
   for (i64 i = 1; i < strtab_offsets.size(); i++)
     strtab_offsets[i] += strtab_offsets[i - 1];
 
@@ -1066,6 +1082,21 @@ void SymtabSection<E>::copy_buf(Context<
   u8 *strtab = ctx.buf + ctx.strtab.hdr.offset;
   strtab[0] = '\0';
 
+  // Create symbols for -add_ast_path
+  {
+    i64 symoff = 0;
+    i64 stroff = 1;
+    for (std::string_view s : ctx.arg.add_ast_path) {
+      MachSym &msym = buf[symoff++];
+      msym.stroff = stroff;
+      msym.n_type = N_AST;
+
+      write_string(strtab + stroff, s);
+      stroff += s.size() + 1;
+    }
+  }
+
+  // Copy symbols from input files to an output file
   Symbol<E> *mh_execute_header = get_symbol(ctx, "__mh_execute_header");
   Symbol<E> *dyld_private = get_symbol(ctx, "__dyld_private");
   Symbol<E> *mh_dylib_header = get_symbol(ctx, "__mh_dylib_header");
@@ -1148,18 +1179,75 @@ void SymtabSection<E>::copy_buf(Context<
 }
 
 template <typename E>
+static bool has_objc_image_info_section(Context<E> &ctx) {
+  return false;
+}
+
+// Create __DATA,__objc_imageinfo section contents by merging input
+// __objc_imageinfo sections.
+template <typename E>
+std::unique_ptr<ObjcImageInfoSection<E>>
+ObjcImageInfoSection<E>::create(Context<E> &ctx) {
+  ObjcImageInfo *first = nullptr;
+
+  for (ObjectFile<E> *file : ctx.objs) {
+    if (file->objc_image_info) {
+      first = file->objc_image_info;
+      break;
+    }
+  }
+
+  if (!first)
+    return nullptr;
+
+  ObjcImageInfo info;
+  info.flags = (first->flags & OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES);
+
+  for (ObjectFile<E> *file : ctx.objs) {
+    if (!file->objc_image_info)
+      continue;
+
+    ObjcImageInfo &info2 = *file->objc_image_info;
+
+    // Make sure that all object files have the same flag.
+    if ((info.flags & OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES) !=
+        (info2.flags & OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES))
+      Error(ctx) << *file << ": incompatible __objc_imageinfo flag";
+
+    // Make sure that all object files have the same Swift version.
+    if (info.swift_version == 0)
+      info.swift_version = info2.swift_version;
+
+    if (info.swift_version != info2.swift_version && info2.swift_version != 0)
+      Error(ctx) << *file << ": incompatible __objc_imageinfo swift version"
+                 << (u32)info.swift_version << " " << (u32)info2.swift_version;
+
+    // swift_lang_version is set to the newest.
+    info.swift_lang_version =
+      std::max<u32>(info.swift_lang_version, info2.swift_lang_version);
+  }
+
+  return std::make_unique<ObjcImageInfoSection<E>>(ctx, info);
+}
+
+template <typename E>
+void ObjcImageInfoSection<E>::copy_buf(Context<E> &ctx) {
+  memcpy(ctx.buf + this->hdr.offset, &contents, sizeof(contents));
+}
+
+template <typename E>
 void CodeSignatureSection<E>::compute_size(Context<E> &ctx) {
   std::string filename = filepath(ctx.arg.final_output).filename();
   i64 filename_size = align_to(filename.size() + 1, 16);
-  i64 num_blocks = align_to(this->hdr.offset, BLOCK_SIZE) / BLOCK_SIZE;
+  i64 num_blocks = align_to(this->hdr.offset, E::page_size) / E::page_size;
   this->hdr.size = sizeof(CodeSignatureHeader) + sizeof(CodeSignatureBlobIndex) +
                    sizeof(CodeSignatureDirectory) + filename_size +
                    num_blocks * SHA256_SIZE;
 }
 
 // A __code_signature section is optional for x86 macOS but mandatory
-// for ARM macOS. The section contains a cryptographic hash for each 4
-// KiB block of an executable or a dylib file. The program loader
+// for ARM macOS. The section contains a cryptographic hash for each
+// memory page of an executable or a dylib file. The program loader
 // verifies the hash values on the initial execution of a binary and
 // will reject it if a hash value does not match.
 template <typename E>
@@ -1171,7 +1259,7 @@ void CodeSignatureSection<E>::write_sign
 
   std::string filename = filepath(ctx.arg.final_output).filename();
   i64 filename_size = align_to(filename.size() + 1, 16);
-  i64 num_blocks = align_to(this->hdr.offset, BLOCK_SIZE) / BLOCK_SIZE;
+  i64 num_blocks = align_to(this->hdr.offset, E::page_size) / E::page_size;
 
   // Fill code-sign header fields
   CodeSignatureHeader &sighdr = *(CodeSignatureHeader *)buf;
@@ -1200,7 +1288,7 @@ void CodeSignatureSection<E>::write_sign
   dir.code_limit = this->hdr.offset;
   dir.hash_size = SHA256_SIZE;
   dir.hash_type = CS_HASHTYPE_SHA256;
-  dir.page_size = std::countr_zero<u64>(BLOCK_SIZE);
+  dir.page_size = std::countr_zero(E::page_size);
   dir.exec_seg_base = ctx.text_seg->cmd.fileoff;
   dir.exec_seg_limit = ctx.text_seg->cmd.filesize;
   if (ctx.output_type == MH_EXECUTE)
@@ -1209,12 +1297,10 @@ void CodeSignatureSection<E>::write_sign
   memcpy(buf, filename.data(), filename.size());
   buf += filename_size;
 
-  // Compute a hash value for each 4 KiB block. The block size must be
-  // 4 KiB, as the macOS kernel supports only that block size for the
-  // ad-hoc code signatures.
+  // Compute a hash value for each block.
   auto compute_hash = [&](i64 i) {
-    u8 *start = ctx.buf + i * BLOCK_SIZE;
-    u8 *end = ctx.buf + std::min<i64>((i + 1) * BLOCK_SIZE, this->hdr.offset);
+    u8 *start = ctx.buf + i * E::page_size;
+    u8 *end = ctx.buf + std::min<i64>((i + 1) * E::page_size, this->hdr.offset);
     SHA256(start, end - start, buf + i * SHA256_SIZE);
   };
 
@@ -1225,7 +1311,7 @@ void CodeSignatureSection<E>::write_sign
 #if __APPLE__
     // Calling msync() with MS_ASYNC speeds up the following msync()
     // with MS_INVALIDATE.
-    msync(ctx.buf + i * BLOCK_SIZE, 1024 * BLOCK_SIZE, MS_ASYNC);
+    msync(ctx.buf + i * E::page_size, 1024 * E::page_size, MS_ASYNC);
 #endif
   }
 
@@ -1245,7 +1331,7 @@ void CodeSignatureSection<E>::write_sign
     // recompute code signatures for the updated blocks.
     ctx.mach_hdr.copy_buf(ctx);
 
-    for (i64 i = 0; i * BLOCK_SIZE < ctx.mach_hdr.hdr.size; i++)
+    for (i64 i = 0; i * E::page_size < ctx.mach_hdr.hdr.size; i++)
       compute_hash(i);
   }
 
@@ -1556,6 +1642,7 @@ void SectCreateSection<E>::copy_buf(Cont
   template class SymtabSection<E>;                      \
   template class StrtabSection<E>;                      \
   template class CodeSignatureSection<E>;               \
+  template class ObjcImageInfoSection<E>;               \
   template class DataInCodeSection<E>;                  \
   template class StubsSection<E>;                       \
   template class StubHelperSection<E>;                  \
diff -pruN 1.3.1+dfsg-1/macho/tapi.cc 1.4.0+dfsg-1/macho/tapi.cc
--- 1.3.1+dfsg-1/macho/tapi.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/macho/tapi.cc	2022-08-04 12:47:21.000000000 +0000
@@ -57,11 +57,19 @@ static bool contains(const std::vector<Y
   return false;
 }
 
+static bool match_arch(const std::vector<YamlNode> &vec, std::string_view arch) {
+  for (const YamlNode &mem : vec)
+    if (auto val = std::get_if<std::string_view>(&mem.data))
+      if (*val == arch || val->starts_with(std::string(arch) + "-"))
+        return true;
+  return false;
+}
+
 template <typename E>
 static std::optional<TextDylib>
 to_tbd(Context<E> &ctx, YamlNode &node, std::string_view arch,
        std::string_view filename) {
-  if (!contains(get_vector(node, "targets"), arch))
+  if (!match_arch(get_vector(node, "targets"), arch))
     return {};
 
   if (ctx.arg.application_extension &&
@@ -75,7 +83,7 @@ to_tbd(Context<E> &ctx, YamlNode &node,
     tbd.install_name = *val;
 
   for (YamlNode &mem : get_vector(node, "reexported-libraries"))
-    if (contains(get_vector(mem, "targets"), arch))
+    if (match_arch(get_vector(mem, "targets"), arch))
       append(tbd.reexported_libs, get_string_vector(mem, "libraries"));
 
   auto concat = [&](const std::string &x, std::string_view y) {
@@ -84,7 +92,7 @@ to_tbd(Context<E> &ctx, YamlNode &node,
 
   for (std::string_view key : {"exports", "reexports"}) {
     for (YamlNode &mem : get_vector(node, key)) {
-      if (contains(get_vector(mem, "targets"), arch)) {
+      if (match_arch(get_vector(mem, "targets"), arch)) {
         merge(tbd.exports, get_string_vector(mem, "symbols"));
         merge(tbd.weak_exports, get_string_vector(mem, "weak-symbols"));
 
@@ -275,12 +283,12 @@ static TextDylib parse(Context<E> &ctx,
 
 template <>
 TextDylib parse_tbd(Context<ARM64> &ctx, MappedFile<Context<ARM64>> *mf) {
-  return parse(ctx, mf, "arm64-macos");
+  return parse(ctx, mf, "arm64");
 }
 
 template <>
 TextDylib parse_tbd(Context<X86_64> &ctx, MappedFile<Context<X86_64>> *mf) {
-  return parse(ctx, mf, "x86_64-macos");
+  return parse(ctx, mf, "x86_64");
 }
 
 } // namespace mold::macho
diff -pruN 1.3.1+dfsg-1/macho/yaml.cc 1.4.0+dfsg-1/macho/yaml.cc
--- 1.3.1+dfsg-1/macho/yaml.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/macho/yaml.cc	2022-08-04 12:47:21.000000000 +0000
@@ -50,6 +50,9 @@ private:
   std::vector<Token> tokens;
 };
 
+// A tokenizer for YAML. YAML represents blocks by indentation. This
+// tokenizer inserts INDENT and DEDENT special tokens before and after
+// each indented text block.
 std::optional<YamlError> YamlParser::tokenize() {
   std::vector<i64> indents = {0};
 
diff -pruN 1.3.1+dfsg-1/Makefile 1.4.0+dfsg-1/Makefile
--- 1.3.1+dfsg-1/Makefile	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/Makefile	2022-08-04 12:47:21.000000000 +0000
@@ -1,4 +1,4 @@
-VERSION = 1.3.1
+VERSION = 1.4.0
 
 PREFIX = /usr/local
 BINDIR = $(PREFIX)/bin
@@ -10,6 +10,15 @@ INSTALL = install
 INSTALL_PROGRAM = $(INSTALL)
 INSTALL_DATA = $(INSTALL) -m 644
 
+OS := $(shell uname -s)
+ARCH := $(shell uname -m)
+
+ifeq ($(OS), Darwin)
+  TESTS := $(wildcard test/macho/*.sh)
+else
+  TESTS := $(wildcard test/elf/*.sh)
+endif
+
 D = $(DESTDIR)
 
 # CXX defaults to `g++`. Rewrite it with a vendor-neutral compiler
@@ -23,10 +32,7 @@ endif
 STRIP = strip
 
 SRCS = $(wildcard *.cc elf/*.cc macho/*.cc)
-OBJS = $(SRCS:%.cc=out/%.o)
-
-OS := $(shell uname -s)
-ARCH := $(shell uname -m)
+OBJS = $(SRCS:%.cc=out/%.o) out/rust-demangle.o
 
 IS_ANDROID = 0
 ifneq ($(findstring -android,$(shell $(CC) -dumpmachine)),)
@@ -39,7 +45,7 @@ CFLAGS = -O2
 CXXFLAGS = -O2
 
 MOLD_CXXFLAGS := -std=c++20 -fno-exceptions -fno-unwind-tables \
-                 -fno-asynchronous-unwind-tables -Ithird-party/xxhash \
+                 -fno-asynchronous-unwind-tables -Ithird-party \
                  -DMOLD_VERSION=\"$(VERSION)\" -DLIBDIR="\"$(LIBDIR)\""
 
 MOLD_LDFLAGS := -pthread -lz -lm -ldl
@@ -132,6 +138,9 @@ mold: $(OBJS) $(MIMALLOC_LIB) $(TBB_LIB)
 mold-wrapper.so: elf/mold-wrapper.c
 	$(CC) $(DEPFLAGS) $(CFLAGS) -fPIC -shared -o $@ $< $(MOLD_WRAPPER_LDFLAGS) $(LDFLAGS)
 
+out/rust-demangle.o: third-party/rust-demangle/rust-demangle.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
 out/%.o: %.cc out/elf/.keep out/macho/.keep
 	$(CXX) $(MOLD_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS) -c -o $@ $<
 
@@ -152,10 +161,11 @@ $(TBB_LIB):
 
 test tests check: all
 ifeq ($(OS), Darwin)
-	$(MAKE) -C test -f Makefile.darwin --no-print-directory
+	@$(MAKE) $(TESTS) --no-print-directory
 else
-	$(MAKE) -C test -f Makefile.linux --no-print-directory --output-sync
+	@$(MAKE) $(TESTS) --no-print-directory --output-sync
 endif
+
 	@if test -t 1; then \
 	  printf '\e[32mPassed all tests\e[0m\n'; \
 	else \
@@ -179,6 +189,20 @@ test-all: all
 	$(MAKE) test-arch TRIPLE=arm-linux-gnueabihf MACHINE=arm
 	$(MAKE) test-arch TRIPLE=riscv64-linux-gnu MACHINE=riscv64
 
+# macOS's GNU make hasn't been updated since 3.8.1 perhaps due a concern
+# of GPLv3. The --output-sync flag was introduced in GNU Make 4.0, so we
+# can't use that flag on macOS.
+#
+# `tail -r | tail -r` is a poor-man's way to enable full buffering on a
+# command output. `tail -r` outputs an input from the last line to the
+# first.
+$(TESTS):
+ifeq ($(OS), Darwin)
+	@set -o pipefail; ./$@ 2>&1 | tail -r | tail -r
+else
+	@./$@
+endif
+
 install: all
 	$(INSTALL) -d $D$(BINDIR)
 	$(INSTALL_PROGRAM) mold $D$(BINDIR)
@@ -216,4 +240,4 @@ test-tsan:
 clean:
 	rm -rf *~ mold mold-wrapper.so out ld ld64 mold-*-linux.tar.gz
 
-.PHONY: all test tests check clean test-arch test-all test-asan test-ubsan test-tsan
+.PHONY: all test tests check clean test-arch test-all test-asan test-ubsan test-tsan $(TESTS)
diff -pruN 1.3.1+dfsg-1/mold.h 1.4.0+dfsg-1/mold.h
--- 1.3.1+dfsg-1/mold.h	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/mold.h	2022-08-04 12:47:21.000000000 +0000
@@ -27,7 +27,7 @@
 #include <vector>
 
 #define XXH_INLINE_ALL 1
-#include <xxhash.h>
+#include <xxhash/xxhash.h>
 
 #ifdef NDEBUG
 #  define unreachable() __builtin_unreachable()
@@ -310,8 +310,16 @@ inline u64 read_uleb(u8 const*&buf) {
   return read_uleb(const_cast<u8 *&>(buf));
 }
 
+inline u64 read_uleb(std::string_view &str) {
+  u8 *start = (u8 *)&str[0];
+  u8 *ptr = start;
+  u64 val = read_uleb(ptr);
+  str = str.substr(ptr - start);
+  return val;
+}
+
 inline i64 uleb_size(u64 val) {
-#pragma unroll
+#pragma GCC unroll 8
   for (int i = 1; i < 9; i++)
     if (val < ((u64)1 << (7 * i)))
       return i;
@@ -327,6 +335,14 @@ std::string_view save_string(C &ctx, con
   return {(char *)buf, str.size()};
 }
 
+inline bool remove_prefix(std::string_view &s, std::string_view prefix) {
+  if (s.starts_with(prefix)) {
+    s = s.substr(prefix.size());
+    return true;
+  }
+  return false;
+}
+
 //
 // Concurrent Map
 //
@@ -508,7 +524,6 @@ private:
 class MultiGlob {
 public:
   bool add(std::string_view pat, u32 val);
-  void compile();
   bool empty() const { return strings.empty(); }
   std::optional<u32> find(std::string_view str);
 
@@ -519,6 +534,7 @@ private:
     std::unique_ptr<TrieNode> children[256];
   };
 
+  void compile();
   void fix_suffix_links(TrieNode &node);
   void fix_values();
 
@@ -526,7 +542,8 @@ private:
   std::unique_ptr<TrieNode> root;
   std::vector<std::pair<Glob, u32>> globs;
   std::vector<u32> values;
-  bool compiled = false;
+  std::once_flag once;
+  bool is_compiled = false;
 };
 
 //
@@ -553,6 +570,7 @@ std::filesystem::path to_abs_path(std::f
 //
 
 std::string_view demangle(std::string_view name);
+std::optional<std::string_view> cpp_demangle(std::string_view name);
 
 //
 // compress.cc
@@ -763,7 +781,8 @@ MappedFile<C> *MappedFile<C>::open(C &ct
 #endif
 
   if (st.st_size > 0) {
-    mf->data = (u8 *)mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    mf->data = (u8 *)mmap(nullptr, st.st_size, PROT_READ | PROT_WRITE,
+                          MAP_PRIVATE, fd, 0);
     if (mf->data == MAP_FAILED)
       Fatal(ctx) << path << ": mmap failed: " << errno_string();
   }
diff -pruN 1.3.1+dfsg-1/multi-glob.cc 1.4.0+dfsg-1/multi-glob.cc
--- 1.3.1+dfsg-1/multi-glob.cc	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/multi-glob.cc	2022-08-04 12:47:21.000000000 +0000
@@ -25,7 +25,7 @@
 namespace mold {
 
 std::optional<u32> MultiGlob::find(std::string_view str) {
-  assert(compiled);
+  std::call_once(once, [&] { compile(); });
   u32 idx = UINT32_MAX;
 
   if (root) {
@@ -83,7 +83,7 @@ static std::string handle_stars(std::str
 }
 
 bool MultiGlob::add(std::string_view pat, u32 val) {
-  assert(!compiled);
+  assert(!is_compiled);
   assert(!pat.empty());
 
   u32 idx = strings.size();
@@ -115,9 +115,7 @@ bool MultiGlob::add(std::string_view pat
 }
 
 void MultiGlob::compile() {
-  assert(!compiled);
-  compiled = true;
-
+  is_compiled = true;
   if (root) {
     fix_suffix_links(*root);
     fix_values();
diff -pruN 1.3.1+dfsg-1/README.md 1.4.0+dfsg-1/README.md
--- 1.3.1+dfsg-1/README.md	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/README.md	2022-08-04 12:47:21.000000000 +0000
@@ -61,7 +61,7 @@ necessary packages. You may want to run
 ```shell
 git clone https://github.com/rui314/mold.git
 cd mold
-git checkout v1.3.1
+git checkout v1.4.0
 make -j$(nproc) CXX=clang++
 sudo make install
 ```
@@ -214,3 +214,16 @@ it. A legally-binding commercial license
 concern. By purchasing a license, you are guaranteed that mold will be
 maintained for you. Please [contact us](mailto:contact@bluewhale.systems)
 for a commercial license inquiry.
+
+## Acknowledgement
+
+We accept donations via [GitHub Sponsors](https://github.com/sponsors/rui314)
+and [OpenCollective](https://opencollective.com/mold-linker).
+We thank you to everybody who sponsors our project. In particular,
+we'd like to acknowledge the following people and organizations who
+have sponsored $128/month or more:
+
+- [300baud](https://github.com/300baud)
+- [Mercury](https://github.com/MercuryTechnologies)
+- [Wei Wu](https://github.com/lazyparser)
+- [Signal Slot Inc.](https://github.com/signal-slot)
diff -pruN 1.3.1+dfsg-1/test/elf/absolute-symbols.sh 1.4.0+dfsg-1/test/elf/absolute-symbols.sh
--- 1.3.1+dfsg-1/test/elf/absolute-symbols.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/absolute-symbols.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/allow-multiple-definition.sh 1.4.0+dfsg-1/test/elf/allow-multiple-definition.sh
--- 1.3.1+dfsg-1/test/elf/allow-multiple-definition.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/allow-multiple-definition.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 echo 'int main() { return 0; }' | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/ar-alignment.sh 1.4.0+dfsg-1/test/elf/ar-alignment.sh
--- 1.3.1+dfsg-1/test/elf/ar-alignment.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/ar-alignment.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/as-needed2.sh 1.4.0+dfsg-1/test/elf/as-needed2.sh
--- 1.3.1+dfsg-1/test/elf/as-needed2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/as-needed2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -shared -fPIC -o $t/a.so -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/as-needed.sh 1.4.0+dfsg-1/test/elf/as-needed.sh
--- 1.3.1+dfsg-1/test/elf/as-needed.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/as-needed.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/as-needed-weak.sh 1.4.0+dfsg-1/test/elf/as-needed-weak.sh
--- 1.3.1+dfsg-1/test/elf/as-needed-weak.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/as-needed-weak.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/auxiliary.sh 1.4.0+dfsg-1/test/elf/auxiliary.sh
--- 1.3.1+dfsg-1/test/elf/auxiliary.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/auxiliary.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -x assembler -
diff -pruN 1.3.1+dfsg-1/test/elf/basic.sh 1.4.0+dfsg-1/test/elf/basic.sh
--- 1.3.1+dfsg-1/test/elf/basic.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/basic.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/bno-symbolic.sh 1.4.0+dfsg-1/test/elf/bno-symbolic.sh
--- 1.3.1+dfsg-1/test/elf/bno-symbolic.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/bno-symbolic.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -fPIC -o$t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/bsymbolic-functions.sh 1.4.0+dfsg-1/test/elf/bsymbolic-functions.sh
--- 1.3.1+dfsg-1/test/elf/bsymbolic-functions.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/bsymbolic-functions.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -fPIC -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/bsymbolic.sh 1.4.0+dfsg-1/test/elf/bsymbolic.sh
--- 1.3.1+dfsg-1/test/elf/bsymbolic.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/bsymbolic.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -fPIC -o$t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/bug178.sh 1.4.0+dfsg-1/test/elf/bug178.sh
--- 1.3.1+dfsg-1/test/elf/bug178.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/bug178.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # Verify that mold does not crash if no object file is included
diff -pruN 1.3.1+dfsg-1/test/elf/build-id.sh 1.4.0+dfsg-1/test/elf/build-id.sh
--- 1.3.1+dfsg-1/test/elf/build-id.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/build-id.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 echo 'int main() { return 0; }' > $t/a.c
diff -pruN 1.3.1+dfsg-1/test/elf/canonical-plt.sh 1.4.0+dfsg-1/test/elf/canonical-plt.sh
--- 1.3.1+dfsg-1/test/elf/canonical-plt.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/canonical-plt.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.so -fPIC -shared -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/cmdline.sh 1.4.0+dfsg-1/test/elf/cmdline.sh
--- 1.3.1+dfsg-1/test/elf/cmdline.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/cmdline.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 { ./mold -zfoo || true; } 2>&1 | grep -q 'unknown command line option: -zfoo'
diff -pruN 1.3.1+dfsg-1/test/elf/color-diagnostics.sh 1.4.0+dfsg-1/test/elf/color-diagnostics.sh
--- 1.3.1+dfsg-1/test/elf/color-diagnostics.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/color-diagnostics.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/comment.sh 1.4.0+dfsg-1/test/elf/comment.sh
--- 1.3.1+dfsg-1/test/elf/comment.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/comment.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/common-archive.sh 1.4.0+dfsg-1/test/elf/common-archive.sh
--- 1.3.1+dfsg-1/test/elf/common-archive.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/common-archive.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fcommon -xc -c -o $t/a.o -
diff -pruN 1.3.1+dfsg-1/test/elf/common-ref.sh 1.4.0+dfsg-1/test/elf/common-ref.sh
--- 1.3.1+dfsg-1/test/elf/common-ref.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/common-ref.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fcommon -xc -c -o $t/a.o -
diff -pruN 1.3.1+dfsg-1/test/elf/common.sh 1.4.0+dfsg-1/test/elf/common.sh
--- 1.3.1+dfsg-1/test/elf/common.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/common.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fcommon -xc -c -o $t/a.o -
diff -pruN 1.3.1+dfsg-1/test/elf/compress-debug-sections.sh 1.4.0+dfsg-1/test/elf/compress-debug-sections.sh
--- 1.3.1+dfsg-1/test/elf/compress-debug-sections.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/compress-debug-sections.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 which dwarfdump >& /dev/null || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/compressed-debug-info-gnu.sh 1.4.0+dfsg-1/test/elf/compressed-debug-info-gnu.sh
--- 1.3.1+dfsg-1/test/elf/compressed-debug-info-gnu.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/compressed-debug-info-gnu.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 which dwarfdump >& /dev/null || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/compressed-debug-info.sh 1.4.0+dfsg-1/test/elf/compressed-debug-info.sh
--- 1.3.1+dfsg-1/test/elf/compressed-debug-info.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/compressed-debug-info.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 which dwarfdump >& /dev/null || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/copyrel-protected.sh 1.4.0+dfsg-1/test/elf/copyrel-protected.sh
--- 1.3.1+dfsg-1/test/elf/copyrel-protected.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/copyrel-protected.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -fno-PIE -
diff -pruN 1.3.1+dfsg-1/test/elf/copyrel-relro.sh 1.4.0+dfsg-1/test/elf/copyrel-relro.sh
--- 1.3.1+dfsg-1/test/elf/copyrel-relro.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/copyrel-relro.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -fno-PIE -
diff -pruN 1.3.1+dfsg-1/test/elf/copyrel.sh 1.4.0+dfsg-1/test/elf/copyrel.sh
--- 1.3.1+dfsg-1/test/elf/copyrel.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/copyrel.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fno-PIC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/dead-debug-sections.sh 1.4.0+dfsg-1/test/elf/dead-debug-sections.sh
--- 1.3.1+dfsg-1/test/elf/dead-debug-sections.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/dead-debug-sections.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 which dwarfdump >& /dev/null || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/debug-macro-section.sh 1.4.0+dfsg-1/test/elf/debug-macro-section.sh
--- 1.3.1+dfsg-1/test/elf/debug-macro-section.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/debug-macro-section.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF > $t/a.h
diff -pruN 1.3.1+dfsg-1/test/elf/default-symver.sh 1.4.0+dfsg-1/test/elf/default-symver.sh
--- 1.3.1+dfsg-1/test/elf/default-symver.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/default-symver.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/defsym.sh 1.4.0+dfsg-1/test/elf/defsym.sh
--- 1.3.1+dfsg-1/test/elf/defsym.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/defsym.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/demangle-rust.sh 1.4.0+dfsg-1/test/elf/demangle-rust.sh
--- 1.3.1+dfsg-1/test/elf/demangle-rust.sh	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/demangle-rust.sh	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,26 @@
+#!/bin/bash
+export LC_ALL=C
+set -e
+CC="${TEST_CC:-cc}"
+CXX="${TEST_CXX:-c++}"
+GCC="${TEST_GCC:-gcc}"
+GXX="${TEST_GXX:-g++}"
+OBJDUMP="${OBJDUMP:-objdump}"
+MACHINE="${MACHINE:-$(uname -m)}"
+testname=$(basename "$0" .sh)
+echo -n "Testing $testname ... "
+t=out/test/elf/$MACHINE/$testname
+mkdir -p $t
+
+[ $MACHINE = x86_64 ] || { echo skipped; exit; }
+
+cat <<'EOF' | $CC -c -o $t/a.o -xassembler -
+.globl main
+main:
+call _RNCINkXs25_NgCsbmNqQUJIY6D_4core5sliceINyB9_4IterhENuNgNoBb_4iter8iterator8Iterator9rpositionNCNgNpB9_6memchr7memrchrs_0E0Bb_
+EOF
+
+! $CC -B. -o $t/exe $t/a.o 2> $t/log || false
+fgrep -q '<core::slice::Iter<u8> as core::iter::iterator::Iterator>::rposition::<core::slice::memchr::memrchr::{closure#1}>::{closure#0}' $t/log
+
+echo OK
diff -pruN 1.3.1+dfsg-1/test/elf/demangle.sh 1.4.0+dfsg-1/test/elf/demangle.sh
--- 1.3.1+dfsg-1/test/elf/demangle.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/demangle.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc++ -
diff -pruN 1.3.1+dfsg-1/test/elf/dependency-file.sh 1.4.0+dfsg-1/test/elf/dependency-file.sh
--- 1.3.1+dfsg-1/test/elf/dependency-file.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/dependency-file.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/disable-new-dtags.sh 1.4.0+dfsg-1/test/elf/disable-new-dtags.sh
--- 1.3.1+dfsg-1/test/elf/disable-new-dtags.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/disable-new-dtags.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -fPIC -
diff -pruN 1.3.1+dfsg-1/test/elf/discard.sh 1.4.0+dfsg-1/test/elf/discard.sh
--- 1.3.1+dfsg-1/test/elf/discard.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/discard.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = riscv64 ] && { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/dso-undef.sh 1.4.0+dfsg-1/test/elf/dso-undef.sh
--- 1.3.1+dfsg-1/test/elf/dso-undef.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/dso-undef.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/dt-init.sh 1.4.0+dfsg-1/test/elf/dt-init.sh
--- 1.3.1+dfsg-1/test/elf/dt-init.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/dt-init.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = riscv64 ] && { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/dt-needed.sh 1.4.0+dfsg-1/test/elf/dt-needed.sh
--- 1.3.1+dfsg-1/test/elf/dt-needed.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/dt-needed.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/duplicate-error.sh 1.4.0+dfsg-1/test/elf/duplicate-error.sh
--- 1.3.1+dfsg-1/test/elf/duplicate-error.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/duplicate-error.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -x assembler -
diff -pruN 1.3.1+dfsg-1/test/elf/dynamic-dt-debug.sh 1.4.0+dfsg-1/test/elf/dynamic-dt-debug.sh
--- 1.3.1+dfsg-1/test/elf/dynamic-dt-debug.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/dynamic-dt-debug.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/dynamic-linker.sh 1.4.0+dfsg-1/test/elf/dynamic-linker.sh
--- 1.3.1+dfsg-1/test/elf/dynamic-linker.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/dynamic-linker.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -x assembler -
diff -pruN 1.3.1+dfsg-1/test/elf/dynamic-list2.sh 1.4.0+dfsg-1/test/elf/dynamic-list2.sh
--- 1.3.1+dfsg-1/test/elf/dynamic-list2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/dynamic-list2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
@@ -33,11 +32,18 @@ cat <<EOF > $t/dyn
 { foo; extern "C++" { "baz(int)"; }; };
 EOF
 
-$CC -B. -o $t/exe $t/a.o $t/b.o -Wl,-dynamic-list=$t/dyn
+$CC -B. -o $t/exe1 $t/a.o $t/b.o -Wl,-dynamic-list=$t/dyn
 
-readelf --dyn-syms $t/exe > $t/log
-grep -q ' foo$' $t/log
-! grep -q ' bar$' $t/log || false
-grep -q ' _Z3bazi$' $t/log
+readelf --dyn-syms $t/exe1 > $t/log1
+grep -q ' foo$' $t/log1
+! grep -q ' bar$' $t/log1 || false
+grep -q ' _Z3bazi$' $t/log1
+
+$CC -B. -o $t/exe2 $t/a.o $t/b.o -Wl,--export-dynamic-symbol-list=$t/dyn
+
+readelf --dyn-syms $t/exe2 > $t/log2
+grep -q ' foo$' $t/log2
+! grep -q ' bar$' $t/log2 || false
+grep -q ' _Z3bazi$' $t/log2
 
 echo OK
diff -pruN 1.3.1+dfsg-1/test/elf/dynamic-list3.sh 1.4.0+dfsg-1/test/elf/dynamic-list3.sh
--- 1.3.1+dfsg-1/test/elf/dynamic-list3.sh	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/dynamic-list3.sh	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,52 @@
+#!/bin/bash
+export LC_ALL=C
+set -e
+CC="${TEST_CC:-cc}"
+CXX="${TEST_CXX:-c++}"
+GCC="${TEST_GCC:-gcc}"
+GXX="${TEST_GXX:-g++}"
+OBJDUMP="${OBJDUMP:-objdump}"
+MACHINE="${MACHINE:-$(uname -m)}"
+testname=$(basename "$0" .sh)
+echo -n "Testing $testname ... "
+t=out/test/elf/$testname
+mkdir -p $t
+
+cat <<'EOF' > $t/dyn
+{
+  xyz;
+  foo*bar*[abc]x;
+};
+EOF
+
+cat <<EOF | $CXX -fPIC -c -o $t/b.o -xc -
+void xyz() {}
+void foobarzx() {}
+void foobarcx() {}
+void foo123bar456bx() {}
+void foo123bar456c() {}
+void foo123bar456x() {}
+int main() {}
+EOF
+
+$CC -B. -Wl,--dynamic-list=$t/dyn -o $t/exe1 $t/b.o
+
+readelf --dyn-syms $t/exe1 > $t/log1
+grep -q ' xyz$' $t/log1
+! grep -q ' foobarzx$' $t/log1 || false
+grep -q ' foobarcx$' $t/log1
+grep -q ' foo123bar456bx$' $t/log1
+! grep -q ' foo123bar456c$' $t/log1 || false
+! grep -q ' foo123bar456x$' $t/log1 || false
+
+$CC -B. -Wl,--export-dynamic-symbol-list=$t/dyn -o $t/exe2 $t/b.o
+
+readelf --dyn-syms $t/exe2 > $t/log2
+grep -q ' xyz$' $t/log2
+! grep -q ' foobarzx$' $t/log2 || false
+grep -q ' foobarcx$' $t/log2
+grep -q ' foo123bar456bx$' $t/log2
+! grep -q ' foo123bar456c$' $t/log2 || false
+! grep -q ' foo123bar456x$' $t/log2 || false
+
+echo OK
diff -pruN 1.3.1+dfsg-1/test/elf/dynamic-list.sh 1.4.0+dfsg-1/test/elf/dynamic-list.sh
--- 1.3.1+dfsg-1/test/elf/dynamic-list.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/dynamic-list.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
@@ -29,10 +28,22 @@ cat <<EOF > $t/dyn
 { foo; bar; };
 EOF
 
-$CC -B. -o $t/exe $t/a.o -Wl,-dynamic-list=$t/dyn
+$CC -B. -o $t/exe1 $t/a.o -Wl,-dynamic-list=$t/dyn
 
-readelf --dyn-syms $t/exe > $t/log
-grep -q ' foo$' $t/log
-grep -q ' bar$' $t/log
+readelf --dyn-syms $t/exe1 > $t/log1
+grep -q ' foo$' $t/log1
+grep -q ' bar$' $t/log1
+
+$CC -B. -o $t/exe2 $t/a.o -Wl,--export-dynamic-symbol-list=$t/dyn
+
+readelf --dyn-syms $t/exe2 > $t/log2
+grep -q ' foo$' $t/log2
+grep -q ' bar$' $t/log2
+
+$CC -B. -o $t/exe3 $t/a.o -Wl,--export-dynamic-symbol=foo,--export-dynamic-symbol=bar
+
+readelf --dyn-syms $t/exe3 > $t/log3
+grep -q ' foo$' $t/log3
+grep -q ' bar$' $t/log3
 
 echo OK
diff -pruN 1.3.1+dfsg-1/test/elf/dynamic.sh 1.4.0+dfsg-1/test/elf/dynamic.sh
--- 1.3.1+dfsg-1/test/elf/dynamic.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/dynamic.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 echo '.globl main; main:' | $CC -o $t/a.o -c -x assembler -
diff -pruN 1.3.1+dfsg-1/test/elf/emit-relocs.sh 1.4.0+dfsg-1/test/elf/emit-relocs.sh
--- 1.3.1+dfsg-1/test/elf/emit-relocs.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/emit-relocs.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/empty-file.sh 1.4.0+dfsg-1/test/elf/empty-file.sh
--- 1.3.1+dfsg-1/test/elf/empty-file.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/empty-file.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/empty-input.sh 1.4.0+dfsg-1/test/elf/empty-input.sh
--- 1.3.1+dfsg-1/test/elf/empty-input.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/empty-input.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,13 +9,12 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 rm -f $t/a.o
 touch $t/a.o
 ! $CC -B. -o $t/exe $t/a.o &> $t/log || false
-grep -q 'unknown file type: EMPTY' $t/log
+grep -q 'unknown file type' $t/log
 
 echo OK
diff -pruN 1.3.1+dfsg-1/test/elf/empty-version.sh 1.4.0+dfsg-1/test/elf/empty-version.sh
--- 1.3.1+dfsg-1/test/elf/empty-version.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/empty-version.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/emulation-deduction.sh 1.4.0+dfsg-1/test/elf/emulation-deduction.sh
--- 1.3.1+dfsg-1/test/elf/emulation-deduction.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/emulation-deduction.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # Skip if target is not x86-64
diff -pruN 1.3.1+dfsg-1/test/elf/entry.sh 1.4.0+dfsg-1/test/elf/entry.sh
--- 1.3.1+dfsg-1/test/elf/entry.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/entry.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/exception-mcmodel-large.sh 1.4.0+dfsg-1/test/elf/exception-mcmodel-large.sh
--- 1.3.1+dfsg-1/test/elf/exception-mcmodel-large.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/exception-mcmodel-large.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/exception.sh 1.4.0+dfsg-1/test/elf/exception.sh
--- 1.3.1+dfsg-1/test/elf/exception.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/exception.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CXX -c -o $t/a.o -xc++ -fPIC -
diff -pruN 1.3.1+dfsg-1/test/elf/exclude-libs2.sh 1.4.0+dfsg-1/test/elf/exclude-libs2.sh
--- 1.3.1+dfsg-1/test/elf/exclude-libs2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/exclude-libs2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -x assembler -c -o $t/a.o -
diff -pruN 1.3.1+dfsg-1/test/elf/exclude-libs3.sh 1.4.0+dfsg-1/test/elf/exclude-libs3.sh
--- 1.3.1+dfsg-1/test/elf/exclude-libs3.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/exclude-libs3.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -xc -c -o $t/a.o -
diff -pruN 1.3.1+dfsg-1/test/elf/exclude-libs.sh 1.4.0+dfsg-1/test/elf/exclude-libs.sh
--- 1.3.1+dfsg-1/test/elf/exclude-libs.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/exclude-libs.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -xc -c -o $t/a.o -
diff -pruN 1.3.1+dfsg-1/test/elf/execstack-if-needed.sh 1.4.0+dfsg-1/test/elf/execstack-if-needed.sh
--- 1.3.1+dfsg-1/test/elf/execstack-if-needed.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/execstack-if-needed.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/execstack.sh 1.4.0+dfsg-1/test/elf/execstack.sh
--- 1.3.1+dfsg-1/test/elf/execstack.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/execstack.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -xc -o $t/a.o -
diff -pruN 1.3.1+dfsg-1/test/elf/export-dynamic.sh 1.4.0+dfsg-1/test/elf/export-dynamic.sh
--- 1.3.1+dfsg-1/test/elf/export-dynamic.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/export-dynamic.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -x assembler -
diff -pruN 1.3.1+dfsg-1/test/elf/export-from-exe.sh 1.4.0+dfsg-1/test/elf/export-from-exe.sh
--- 1.3.1+dfsg-1/test/elf/export-from-exe.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/export-from-exe.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/fatal-warnings.sh 1.4.0+dfsg-1/test/elf/fatal-warnings.sh
--- 1.3.1+dfsg-1/test/elf/fatal-warnings.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/fatal-warnings.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fcommon -xc -c -o $t/a.o -
diff -pruN 1.3.1+dfsg-1/test/elf/filler.sh 1.4.0+dfsg-1/test/elf/filler.sh
--- 1.3.1+dfsg-1/test/elf/filler.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/filler.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/filter.sh 1.4.0+dfsg-1/test/elf/filter.sh
--- 1.3.1+dfsg-1/test/elf/filter.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/filter.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -x assembler -
diff -pruN 1.3.1+dfsg-1/test/elf/func-addr.sh 1.4.0+dfsg-1/test/elf/func-addr.sh
--- 1.3.1+dfsg-1/test/elf/func-addr.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/func-addr.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -shared -o $t/a.so -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/gc-sections.sh 1.4.0+dfsg-1/test/elf/gc-sections.sh
--- 1.3.1+dfsg-1/test/elf/gc-sections.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/gc-sections.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF > $t/a.cc
diff -pruN 1.3.1+dfsg-1/test/elf/gdb-index-compress-output.sh 1.4.0+dfsg-1/test/elf/gdb-index-compress-output.sh
--- 1.3.1+dfsg-1/test/elf/gdb-index-compress-output.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/gdb-index-compress-output.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = $(uname -m) ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/gdb-index-dwarf2.sh 1.4.0+dfsg-1/test/elf/gdb-index-dwarf2.sh
--- 1.3.1+dfsg-1/test/elf/gdb-index-dwarf2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/gdb-index-dwarf2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = $(uname -m) ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/gdb-index-dwarf3.sh 1.4.0+dfsg-1/test/elf/gdb-index-dwarf3.sh
--- 1.3.1+dfsg-1/test/elf/gdb-index-dwarf3.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/gdb-index-dwarf3.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = $(uname -m) ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/gdb-index-dwarf4.sh 1.4.0+dfsg-1/test/elf/gdb-index-dwarf4.sh
--- 1.3.1+dfsg-1/test/elf/gdb-index-dwarf4.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/gdb-index-dwarf4.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = $(uname -m) ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/gdb-index-dwarf5.sh 1.4.0+dfsg-1/test/elf/gdb-index-dwarf5.sh
--- 1.3.1+dfsg-1/test/elf/gdb-index-dwarf5.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/gdb-index-dwarf5.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = $(uname -m) ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/gdb-index-empty.sh 1.4.0+dfsg-1/test/elf/gdb-index-empty.sh
--- 1.3.1+dfsg-1/test/elf/gdb-index-empty.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/gdb-index-empty.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 echo 'void _start() {}' | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/glibc-2.22-bug.sh 1.4.0+dfsg-1/test/elf/glibc-2.22-bug.sh
--- 1.3.1+dfsg-1/test/elf/glibc-2.22-bug.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/glibc-2.22-bug.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # glibc 2.22 or prior have a bug that ld-linux.so.2 crashes on dlopen()
diff -pruN 1.3.1+dfsg-1/test/elf/gnu-hash.sh 1.4.0+dfsg-1/test/elf/gnu-hash.sh
--- 1.3.1+dfsg-1/test/elf/gnu-hash.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/gnu-hash.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/gnu-linkonce.sh 1.4.0+dfsg-1/test/elf/gnu-linkonce.sh
--- 1.3.1+dfsg-1/test/elf/gnu-linkonce.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/gnu-linkonce.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
@@ -34,6 +33,6 @@ int main() {}
 EOF
 
 $CC -B. -o $t/exe $t/a.o $t/b.o $t/c.o
-$OBJDUMP -d $t/exe | grep -A1 '<__x86.get_pc_thunk.bx>:' | grep -q puts
+$OBJDUMP -d $t/exe | grep -A1 '<__x86.get_pc_thunk.bx>:' | fgrep -q .plt
 
 echo OK
diff -pruN 1.3.1+dfsg-1/test/elf/gnu-retain.sh 1.4.0+dfsg-1/test/elf/gnu-retain.sh
--- 1.3.1+dfsg-1/test/elf/gnu-retain.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/gnu-retain.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/gnu-unique.sh 1.4.0+dfsg-1/test/elf/gnu-unique.sh
--- 1.3.1+dfsg-1/test/elf/gnu-unique.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/gnu-unique.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 which $GXX >& /dev/null || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/gnu-warning.sh 1.4.0+dfsg-1/test/elf/gnu-warning.sh
--- 1.3.1+dfsg-1/test/elf/gnu-warning.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/gnu-warning.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $GCC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/hello-dynamic.sh 1.4.0+dfsg-1/test/elf/hello-dynamic.sh
--- 1.3.1+dfsg-1/test/elf/hello-dynamic.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/hello-dynamic.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/hello-static.sh 1.4.0+dfsg-1/test/elf/hello-static.sh
--- 1.3.1+dfsg-1/test/elf/hello-static.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/hello-static.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/help.sh 1.4.0+dfsg-1/test/elf/help.sh
--- 1.3.1+dfsg-1/test/elf/help.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/help.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 ./mold --help | grep -q Usage
diff -pruN 1.3.1+dfsg-1/test/elf/hidden-undef.sh 1.4.0+dfsg-1/test/elf/hidden-undef.sh
--- 1.3.1+dfsg-1/test/elf/hidden-undef.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/hidden-undef.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.so -shared -fPIC -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/icf.sh 1.4.0+dfsg-1/test/elf/icf.sh
--- 1.3.1+dfsg-1/test/elf/icf.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/icf.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -ffunction-sections -fdata-sections -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/icf-small.sh 1.4.0+dfsg-1/test/elf/icf-small.sh
--- 1.3.1+dfsg-1/test/elf/icf-small.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/icf-small.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -ffunction-sections -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/ifunc-dso.sh 1.4.0+dfsg-1/test/elf/ifunc-dso.sh
--- 1.3.1+dfsg-1/test/elf/ifunc-dso.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/ifunc-dso.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,16 +9,14 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # IFUNC is not supported on RISC-V yet
 [ $MACHINE = riscv64 ] && { echo skipped; exit; }
 
 # Skip if libc is musl because musl does not support GNU FUNC
-echo 'int main() {}' | $CC -o $t/exe -xc -
-readelf --dynamic $t/exe | grep -q ld-musl && { echo OK; exit; }
+ldd --help 2>&1 | grep -q musl && { echo skipped; exit; }
 
 cat <<EOF | $CC -fPIC -o $t/a.o -c -xc -
 void foobar(void);
diff -pruN 1.3.1+dfsg-1/test/elf/ifunc-dynamic.sh 1.4.0+dfsg-1/test/elf/ifunc-dynamic.sh
--- 1.3.1+dfsg-1/test/elf/ifunc-dynamic.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/ifunc-dynamic.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,16 +9,14 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # IFUNC is not supported on RISC-V yet
 [ $MACHINE = riscv64 ] && { echo skipped; exit; }
 
 # Skip if libc is musl because musl does not support GNU FUNC
-echo 'int main() {}' | $CC -o $t/exe -xc -
-readelf --dynamic $t/exe | grep -q ld-musl && { echo OK; exit; }
+ldd --help 2>&1 | grep -q musl && { echo skipped; exit; }
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
 #include <stdio.h>
diff -pruN 1.3.1+dfsg-1/test/elf/ifunc-export.sh 1.4.0+dfsg-1/test/elf/ifunc-export.sh
--- 1.3.1+dfsg-1/test/elf/ifunc-export.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/ifunc-export.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,16 +9,14 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # IFUNC is not supported on RISC-V yet
 [ $MACHINE = riscv64 ] && { echo skipped; exit; }
 
 # Skip if libc is musl because musl does not support GNU FUNC
-echo 'int main() {}' | $CC -o $t/exe -xc -
-readelf --dynamic $t/exe | grep -q ld-musl && { echo OK; exit; }
+ldd --help 2>&1 | grep -q musl && { echo skipped; exit; }
 
 cat <<EOF | $CC -c -fPIC -o $t/a.o -xc -
 #include <stdio.h>
diff -pruN 1.3.1+dfsg-1/test/elf/ifunc-static-pie.sh 1.4.0+dfsg-1/test/elf/ifunc-static-pie.sh
--- 1.3.1+dfsg-1/test/elf/ifunc-static-pie.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/ifunc-static-pie.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,13 +9,14 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
+# Skip if libc is musl because musl does not support GNU FUNC
+ldd --help 2>&1 | grep -q musl && { echo skipped; exit; }
+
 # We need to implement R_386_GOT32X relaxation to support PIE on i386
-[ $MACHINE = i386 ] && { echo skipped; exit; }
-[ $MACHINE = i686 ] && { echo skipped; exit; }
+[ $MACHINE = i386 -o $MACHINE = i686 ] && { echo skipped; exit; }
 
 # RISCV64 does not support IFUNC yet
 [ $MACHINE = riscv64 ] && { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/ifunc-static.sh 1.4.0+dfsg-1/test/elf/ifunc-static.sh
--- 1.3.1+dfsg-1/test/elf/ifunc-static.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/ifunc-static.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,16 +9,14 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # IFUNC is not supported on RISC-V yet
 [ $MACHINE = riscv64 ] && { echo skipped; exit; }
 
 # Skip if libc is musl because musl does not support GNU FUNC
-echo 'int main() {}' | $CC -o $t/exe -xc -
-readelf --dynamic $t/exe | grep -q ld-musl && { echo OK; exit; }
+ldd --help 2>&1 | grep -q musl && { echo skipped; exit; }
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
 #include <stdio.h>
diff -pruN 1.3.1+dfsg-1/test/elf/image-base.sh 1.4.0+dfsg-1/test/elf/image-base.sh
--- 1.3.1+dfsg-1/test/elf/image-base.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/image-base.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/incompatible-libs2.sh 1.4.0+dfsg-1/test/elf/incompatible-libs2.sh
--- 1.3.1+dfsg-1/test/elf/incompatible-libs2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/incompatible-libs2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/incompatible-libs.sh 1.4.0+dfsg-1/test/elf/incompatible-libs.sh
--- 1.3.1+dfsg-1/test/elf/incompatible-libs.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/incompatible-libs.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/incompatible-obj.sh 1.4.0+dfsg-1/test/elf/incompatible-obj.sh
--- 1.3.1+dfsg-1/test/elf/incompatible-obj.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/incompatible-obj.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/init-array-priorities.sh 1.4.0+dfsg-1/test/elf/init-array-priorities.sh
--- 1.3.1+dfsg-1/test/elf/init-array-priorities.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/init-array-priorities.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,13 +9,11 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # musl does not support GNU-style init/fini priorities
-echo 'int main() {}' | $CC -o $t/exe -xc -
-readelf --dynamic $t/exe | grep -q ld-musl && { echo skipped; exit; }
+ldd --help 2>&1 | grep -q musl && { echo skipped; exit; }
 
 cat <<'EOF' | $CC -c -o $t/a.o -xc -
 #include <stdio.h>
diff -pruN 1.3.1+dfsg-1/test/elf/init-array-readonly.sh 1.4.0+dfsg-1/test/elf/init-array-readonly.sh
--- 1.3.1+dfsg-1/test/elf/init-array-readonly.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/init-array-readonly.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/init-array.sh 1.4.0+dfsg-1/test/elf/init-array.sh
--- 1.3.1+dfsg-1/test/elf/init-array.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/init-array.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/initfirst.sh 1.4.0+dfsg-1/test/elf/initfirst.sh
--- 1.3.1+dfsg-1/test/elf/initfirst.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/initfirst.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -fPIC -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/init-in-dso.sh 1.4.0+dfsg-1/test/elf/init-in-dso.sh
--- 1.3.1+dfsg-1/test/elf/init-in-dso.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/init-in-dso.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -shared -o $t/a.so -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/init.sh 1.4.0+dfsg-1/test/elf/init.sh
--- 1.3.1+dfsg-1/test/elf/init.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/init.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/interpose.sh 1.4.0+dfsg-1/test/elf/interpose.sh
--- 1.3.1+dfsg-1/test/elf/interpose.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/interpose.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -fPIC -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/invalid-version-script.sh 1.4.0+dfsg-1/test/elf/invalid-version-script.sh
--- 1.3.1+dfsg-1/test/elf/invalid-version-script.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/invalid-version-script.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 echo 'int main() {}' | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/large-alignment-dso.sh 1.4.0+dfsg-1/test/elf/large-alignment-dso.sh
--- 1.3.1+dfsg-1/test/elf/large-alignment-dso.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/large-alignment-dso.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,11 +9,11 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
-[ $MACHINE = i386 -o $MACHINE = arm ] && { echo skipped; exit; }
+[ $MACHINE = i386 -o $MACHINE = i686 ] && { echo skipped; exit; }
+[ $MACHINE = arm ] && { echo skipped; exit; }
 
 cat <<EOF | $CC -o $t/a.o -c -xc - -ffunction-sections -fPIC
 #include <stdio.h>
diff -pruN 1.3.1+dfsg-1/test/elf/large-alignment.sh 1.4.0+dfsg-1/test/elf/large-alignment.sh
--- 1.3.1+dfsg-1/test/elf/large-alignment.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/large-alignment.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,11 +9,11 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
-[ $MACHINE = i386 -o $MACHINE = arm ] && { echo skipped; exit; }
+[ $MACHINE = i386 -o $MACHINE = i686 ] && { echo skipped; exit; }
+[ $MACHINE = arm ] && { echo skipped; exit; }
 
 cat <<EOF | $CC -o $t/a.o -c -xc - -ffunction-sections
 #include <stdio.h>
diff -pruN 1.3.1+dfsg-1/test/elf/linker-script2.sh 1.4.0+dfsg-1/test/elf/linker-script2.sh
--- 1.3.1+dfsg-1/test/elf/linker-script2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/linker-script2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/linker-script3.sh 1.4.0+dfsg-1/test/elf/linker-script3.sh
--- 1.3.1+dfsg-1/test/elf/linker-script3.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/linker-script3.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 mkdir -p $t/foo
diff -pruN 1.3.1+dfsg-1/test/elf/linker-script4.sh 1.4.0+dfsg-1/test/elf/linker-script4.sh
--- 1.3.1+dfsg-1/test/elf/linker-script4.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/linker-script4.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 echo 'VERSION { ver_x { global: *; }; };' > $t/a.script
diff -pruN 1.3.1+dfsg-1/test/elf/linker-script.sh 1.4.0+dfsg-1/test/elf/linker-script.sh
--- 1.3.1+dfsg-1/test/elf/linker-script.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/linker-script.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/link-order.sh 1.4.0+dfsg-1/test/elf/link-order.sh
--- 1.3.1+dfsg-1/test/elf/link-order.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/link-order.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/lto-archive.sh 1.4.0+dfsg-1/test/elf/lto-archive.sh
--- 1.3.1+dfsg-1/test/elf/lto-archive.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/lto-archive.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,12 +9,14 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ "$CC" = cc ] || { echo skipped; exit; }
 
+echo 'int main() {}' | $CC -flto -o /dev/null -xc - >& /dev/null \
+  || { echo skipped; exit; }
+
 cat <<EOF | $CC -o $t/a.o -c -flto -xc -
 #include <stdio.h>
 void hello() {
diff -pruN 1.3.1+dfsg-1/test/elf/lto-dso.sh 1.4.0+dfsg-1/test/elf/lto-dso.sh
--- 1.3.1+dfsg-1/test/elf/lto-dso.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/lto-dso.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,10 +9,12 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
+echo 'int main() {}' | $CC -flto -o /dev/null -xc - >& /dev/null \
+  || { echo skipped; exit; }
+
 cat <<EOF | $CC -flto -c -fPIC -o $t/a.o -xc -
 void foo() {}
 EOF
diff -pruN 1.3.1+dfsg-1/test/elf/lto-gcc.sh 1.4.0+dfsg-1/test/elf/lto-gcc.sh
--- 1.3.1+dfsg-1/test/elf/lto-gcc.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/lto-gcc.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,11 +9,11 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
-which $GCC >& /dev/null || { echo skipped; exit; }
+echo 'int main() {}' | $GCC -flto -o /dev/null -xc - >& /dev/null \
+  || { echo skipped; exit; }
 
 cat <<EOF | $GCC -flto -c -o $t/a.o -xc -
 #include <stdio.h>
diff -pruN 1.3.1+dfsg-1/test/elf/lto-llvm.sh 1.4.0+dfsg-1/test/elf/lto-llvm.sh
--- 1.3.1+dfsg-1/test/elf/lto-llvm.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/lto-llvm.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,13 +9,13 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = $(uname -m) ] || { echo skipped; exit; }
 
-which clang >& /dev/null || { echo skipped; exit; }
+echo 'int main() {}' | clang -flto -o /dev/null -xc - >& /dev/null \
+  || { echo skipped; exit; }
 
 cat <<EOF | clang -flto -c -o $t/a.o -xc -
 #include <stdio.h>
diff -pruN 1.3.1+dfsg-1/test/elf/lto-version-script.sh 1.4.0+dfsg-1/test/elf/lto-version-script.sh
--- 1.3.1+dfsg-1/test/elf/lto-version-script.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/lto-version-script.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -flto -c -fPIC -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/many-sections.sh 1.4.0+dfsg-1/test/elf/many-sections.sh
--- 1.3.1+dfsg-1/test/elf/many-sections.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/many-sections.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/mergeable-records.sh 1.4.0+dfsg-1/test/elf/mergeable-records.sh
--- 1.3.1+dfsg-1/test/elf/mergeable-records.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/mergeable-records.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # Skip if target is not x86-64
diff -pruN 1.3.1+dfsg-1/test/elf/mergeable-strings.sh 1.4.0+dfsg-1/test/elf/mergeable-strings.sh
--- 1.3.1+dfsg-1/test/elf/mergeable-strings.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/mergeable-strings.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # Skip if target is not x86-64
diff -pruN 1.3.1+dfsg-1/test/elf/missing-but-ok.sh 1.4.0+dfsg-1/test/elf/missing-but-ok.sh
--- 1.3.1+dfsg-1/test/elf/missing-but-ok.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/missing-but-ok.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -x assembler -
diff -pruN 1.3.1+dfsg-1/test/elf/missing-error.sh 1.4.0+dfsg-1/test/elf/missing-error.sh
--- 1.3.1+dfsg-1/test/elf/missing-error.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/missing-error.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/mold-wrapper2.sh 1.4.0+dfsg-1/test/elf/mold-wrapper2.sh
--- 1.3.1+dfsg-1/test/elf/mold-wrapper2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/mold-wrapper2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 ldd mold-wrapper.so | grep -q libasan && { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/mold-wrapper.sh 1.4.0+dfsg-1/test/elf/mold-wrapper.sh
--- 1.3.1+dfsg-1/test/elf/mold-wrapper.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/mold-wrapper.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ "$CC" = cc ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/nocopyreloc.sh 1.4.0+dfsg-1/test/elf/nocopyreloc.sh
--- 1.3.1+dfsg-1/test/elf/nocopyreloc.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/nocopyreloc.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -shared -o $t/a.so -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/noinhibit-exec.sh 1.4.0+dfsg-1/test/elf/noinhibit-exec.sh
--- 1.3.1+dfsg-1/test/elf/noinhibit-exec.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/noinhibit-exec.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc - -fno-PIC
diff -pruN 1.3.1+dfsg-1/test/elf/non-canonical-plt.sh 1.4.0+dfsg-1/test/elf/non-canonical-plt.sh
--- 1.3.1+dfsg-1/test/elf/non-canonical-plt.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/non-canonical-plt.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.so -fPIC -shared -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/no-quick-exit.sh 1.4.0+dfsg-1/test/elf/no-quick-exit.sh
--- 1.3.1+dfsg-1/test/elf/no-quick-exit.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/no-quick-exit.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/nostdlib.sh 1.4.0+dfsg-1/test/elf/nostdlib.sh
--- 1.3.1+dfsg-1/test/elf/nostdlib.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/nostdlib.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc - -fno-PIE
diff -pruN 1.3.1+dfsg-1/test/elf/note2.sh 1.4.0+dfsg-1/test/elf/note2.sh
--- 1.3.1+dfsg-1/test/elf/note2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/note2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/note-property.sh 1.4.0+dfsg-1/test/elf/note-property.sh
--- 1.3.1+dfsg-1/test/elf/note-property.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/note-property.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # Skip if target is not x86-64
diff -pruN 1.3.1+dfsg-1/test/elf/note.sh 1.4.0+dfsg-1/test/elf/note.sh
--- 1.3.1+dfsg-1/test/elf/note.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/note.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/now.sh 1.4.0+dfsg-1/test/elf/now.sh
--- 1.3.1+dfsg-1/test/elf/now.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/now.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -fPIC -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/oformat-binary.sh 1.4.0+dfsg-1/test/elf/oformat-binary.sh
--- 1.3.1+dfsg-1/test/elf/oformat-binary.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/oformat-binary.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc - -fno-PIE
diff -pruN 1.3.1+dfsg-1/test/elf/omagic.sh 1.4.0+dfsg-1/test/elf/omagic.sh
--- 1.3.1+dfsg-1/test/elf/omagic.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/omagic.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc - -fno-PIC
diff -pruN 1.3.1+dfsg-1/test/elf/package-metadata.sh 1.4.0+dfsg-1/test/elf/package-metadata.sh
--- 1.3.1+dfsg-1/test/elf/package-metadata.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/package-metadata.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/pack-dyn-relocs-relr.sh 1.4.0+dfsg-1/test/elf/pack-dyn-relocs-relr.sh
--- 1.3.1+dfsg-1/test/elf/pack-dyn-relocs-relr.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/pack-dyn-relocs-relr.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 which llvm-readelf >& /dev/null || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/pie.sh 1.4.0+dfsg-1/test/elf/pie.sh
--- 1.3.1+dfsg-1/test/elf/pie.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/pie.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -fPIE -
diff -pruN 1.3.1+dfsg-1/test/elf/plt-dso.sh 1.4.0+dfsg-1/test/elf/plt-dso.sh
--- 1.3.1+dfsg-1/test/elf/plt-dso.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/plt-dso.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/pltgot.sh 1.4.0+dfsg-1/test/elf/pltgot.sh
--- 1.3.1+dfsg-1/test/elf/pltgot.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/pltgot.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
@@ -36,6 +35,6 @@ EOF
 
 $OBJDUMP -d -j .plt.got $t/exe > $t/log
 
-grep -Eq '1020:.*jmp.* <ext2>' $t/log
+grep -Eq '1034:.*jmp.* <ext2>' $t/log
 
 echo OK
diff -pruN 1.3.1+dfsg-1/test/elf/plt.sh 1.4.0+dfsg-1/test/elf/plt.sh
--- 1.3.1+dfsg-1/test/elf/plt.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/plt.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/preinit-array.sh 1.4.0+dfsg-1/test/elf/preinit-array.sh
--- 1.3.1+dfsg-1/test/elf/preinit-array.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/preinit-array.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,12 +9,10 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
-echo 'int main() {}' | $CC -o $t/exe -xc -
-readelf --dynamic $t/exe | grep -q ld-musl && { echo skipped; exit; }
+ldd --help 2>&1 | grep -q musl && { echo skipped; exit; }
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
 
diff -pruN 1.3.1+dfsg-1/test/elf/print-dependencies.sh 1.4.0+dfsg-1/test/elf/print-dependencies.sh
--- 1.3.1+dfsg-1/test/elf/print-dependencies.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/print-dependencies.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/protected-dynsym.sh 1.4.0+dfsg-1/test/elf/protected-dynsym.sh
--- 1.3.1+dfsg-1/test/elf/protected-dynsym.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/protected-dynsym.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/protected.sh 1.4.0+dfsg-1/test/elf/protected.sh
--- 1.3.1+dfsg-1/test/elf/protected.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/protected.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/push-pop-state.sh 1.4.0+dfsg-1/test/elf/push-pop-state.sh
--- 1.3.1+dfsg-1/test/elf/push-pop-state.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/push-pop-state.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -shared -o $t/a.so -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/relax.sh 1.4.0+dfsg-1/test/elf/relax.sh
--- 1.3.1+dfsg-1/test/elf/relax.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/relax.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # Skip if target is not x86-64
diff -pruN 1.3.1+dfsg-1/test/elf/relocatable-archive.sh 1.4.0+dfsg-1/test/elf/relocatable-archive.sh
--- 1.3.1+dfsg-1/test/elf/relocatable-archive.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/relocatable-archive.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/relocatable.sh 1.4.0+dfsg-1/test/elf/relocatable.sh
--- 1.3.1+dfsg-1/test/elf/relocatable.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/relocatable.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,12 +9,11 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98667
-[ $MACHINE = i386 ] && { echo skipped; exit; }
+[ $MACHINE = i386 -o $MACHINE = i686 ] && { echo skipped; exit; }
 
 cat <<EOF | $CXX -c -o $t/a.o -xc++ -
 int one() { return 1; }
diff -pruN 1.3.1+dfsg-1/test/elf/reloc-overflow.sh 1.4.0+dfsg-1/test/elf/reloc-overflow.sh
--- 1.3.1+dfsg-1/test/elf/reloc-overflow.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/reloc-overflow.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/reloc-rodata.sh 1.4.0+dfsg-1/test/elf/reloc-rodata.sh
--- 1.3.1+dfsg-1/test/elf/reloc-rodata.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/reloc-rodata.sh	2022-08-04 12:47:21.000000000 +0000
@@ -10,8 +10,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = aarch64 ] && { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/reloc.sh 1.4.0+dfsg-1/test/elf/reloc.sh
--- 1.3.1+dfsg-1/test/elf/reloc.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/reloc.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/reloc-zero.sh 1.4.0+dfsg-1/test/elf/reloc-zero.sh
--- 1.3.1+dfsg-1/test/elf/reloc-zero.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/reloc-zero.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/relro.sh 1.4.0+dfsg-1/test/elf/relro.sh
--- 1.3.1+dfsg-1/test/elf/relro.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/relro.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -xc -o $t/a.o -
diff -pruN 1.3.1+dfsg-1/test/elf/repro.sh 1.4.0+dfsg-1/test/elf/repro.sh
--- 1.3.1+dfsg-1/test/elf/repro.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/repro.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/require-defined.sh 1.4.0+dfsg-1/test/elf/require-defined.sh
--- 1.3.1+dfsg-1/test/elf/require-defined.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/require-defined.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/response-file.sh 1.4.0+dfsg-1/test/elf/response-file.sh
--- 1.3.1+dfsg-1/test/elf/response-file.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/response-file.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/retain-symbols-file.sh 1.4.0+dfsg-1/test/elf/retain-symbols-file.sh
--- 1.3.1+dfsg-1/test/elf/retain-symbols-file.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/retain-symbols-file.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/reverse-sections.sh 1.4.0+dfsg-1/test/elf/reverse-sections.sh
--- 1.3.1+dfsg-1/test/elf/reverse-sections.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/reverse-sections.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc++ -
diff -pruN 1.3.1+dfsg-1/test/elf/rodata-name.sh 1.4.0+dfsg-1/test/elf/rodata-name.sh
--- 1.3.1+dfsg-1/test/elf/rodata-name.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/rodata-name.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<'EOF' | $CC -c -o $t/a.o -x assembler -
diff -pruN 1.3.1+dfsg-1/test/elf/rosegment.sh 1.4.0+dfsg-1/test/elf/rosegment.sh
--- 1.3.1+dfsg-1/test/elf/rosegment.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/rosegment.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/rpath.sh 1.4.0+dfsg-1/test/elf/rpath.sh
--- 1.3.1+dfsg-1/test/elf/rpath.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/rpath.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -x assembler -
diff -pruN 1.3.1+dfsg-1/test/elf/run-clang.sh 1.4.0+dfsg-1/test/elf/run-clang.sh
--- 1.3.1+dfsg-1/test/elf/run-clang.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/run-clang.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ "$CC" = cc ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/run.sh 1.4.0+dfsg-1/test/elf/run.sh
--- 1.3.1+dfsg-1/test/elf/run.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/run.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ "$CC" = cc ] || { echo skipped; exit; }
@@ -39,7 +38,8 @@ grep -q mold $t/log
 ./mold -run /usr/bin/ld.gold --version | grep -q mold
 
 rm -f $t/ld $t/ld.lld $t/ld.gold $t/foo.ld
-touch $t/ld $t/ld.lld $t/ld.gold $t/foo.ld
+touch $t/ld $t/ld.lld $t/ld.gold
+echo "#!/bin/sh" >$t/foo.ld
 chmod 755 $t/ld $t/ld.lld $t/ld.gold $t/foo.ld
 
 ./mold -run $t/ld --version | grep -q mold
diff -pruN 1.3.1+dfsg-1/test/elf/section-alignment.sh 1.4.0+dfsg-1/test/elf/section-alignment.sh
--- 1.3.1+dfsg-1/test/elf/section-alignment.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/section-alignment.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/section-name.sh 1.4.0+dfsg-1/test/elf/section-name.sh
--- 1.3.1+dfsg-1/test/elf/section-name.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/section-name.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/section-start.sh 1.4.0+dfsg-1/test/elf/section-start.sh
--- 1.3.1+dfsg-1/test/elf/section-start.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/section-start.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -fno-PIC -
diff -pruN 1.3.1+dfsg-1/test/elf/shared.sh 1.4.0+dfsg-1/test/elf/shared.sh
--- 1.3.1+dfsg-1/test/elf/shared.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/shared.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<'EOF' | $CC -fPIC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/shuffle-sections-seed.sh 1.4.0+dfsg-1/test/elf/shuffle-sections-seed.sh
--- 1.3.1+dfsg-1/test/elf/shuffle-sections-seed.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/shuffle-sections-seed.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -ffunction-sections -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/shuffle-sections.sh 1.4.0+dfsg-1/test/elf/shuffle-sections.sh
--- 1.3.1+dfsg-1/test/elf/shuffle-sections.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/shuffle-sections.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -ffunction-sections -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/soname.sh 1.4.0+dfsg-1/test/elf/soname.sh
--- 1.3.1+dfsg-1/test/elf/soname.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/soname.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/start-lib.sh 1.4.0+dfsg-1/test/elf/start-lib.sh
--- 1.3.1+dfsg-1/test/elf/start-lib.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/start-lib.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/start-stop-symbol.sh 1.4.0+dfsg-1/test/elf/start-stop-symbol.sh
--- 1.3.1+dfsg-1/test/elf/start-stop-symbol.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/start-stop-symbol.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<'EOF' | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/static-archive.sh 1.4.0+dfsg-1/test/elf/static-archive.sh
--- 1.3.1+dfsg-1/test/elf/static-archive.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/static-archive.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/long-long-long-filename.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/static-pie.sh 1.4.0+dfsg-1/test/elf/static-pie.sh
--- 1.3.1+dfsg-1/test/elf/static-pie.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/static-pie.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,13 +9,11 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # We need to implement R_386_GOT32X relaxation to support PIE on i386
-[ $MACHINE = i386 ] && { echo skipped; exit; }
-[ $MACHINE = i686 ] && { echo skipped; exit; }
+[ $MACHINE = i386 -o $MACHINE = i686 ] && { echo skipped; exit; }
 
 [ $MACHINE = aarch64 ] && { echo skipped; exit; }
 
diff -pruN 1.3.1+dfsg-1/test/elf/stdout.sh 1.4.0+dfsg-1/test/elf/stdout.sh
--- 1.3.1+dfsg-1/test/elf/stdout.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/stdout.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/strip.sh 1.4.0+dfsg-1/test/elf/strip.sh
--- 1.3.1+dfsg-1/test/elf/strip.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/strip.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<'EOF' | $CC -x assembler -c -o $t/a.o -Wa,-L -
diff -pruN 1.3.1+dfsg-1/test/elf/symbol-rank.sh 1.4.0+dfsg-1/test/elf/symbol-rank.sh
--- 1.3.1+dfsg-1/test/elf/symbol-rank.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/symbol-rank.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/symbol-version2.sh 1.4.0+dfsg-1/test/elf/symbol-version2.sh
--- 1.3.1+dfsg-1/test/elf/symbol-version2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/symbol-version2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/symbol-version3.sh 1.4.0+dfsg-1/test/elf/symbol-version3.sh
--- 1.3.1+dfsg-1/test/elf/symbol-version3.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/symbol-version3.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/symbol-version.sh 1.4.0+dfsg-1/test/elf/symbol-version.sh
--- 1.3.1+dfsg-1/test/elf/symbol-version.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/symbol-version.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/symtab-dso.sh 1.4.0+dfsg-1/test/elf/symtab-dso.sh
--- 1.3.1+dfsg-1/test/elf/symtab-dso.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/symtab-dso.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/symtab-section-symbols.sh 1.4.0+dfsg-1/test/elf/symtab-section-symbols.sh
--- 1.3.1+dfsg-1/test/elf/symtab-section-symbols.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/symtab-section-symbols.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/symtab.sh 1.4.0+dfsg-1/test/elf/symtab.sh
--- 1.3.1+dfsg-1/test/elf/symtab.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/symtab.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -x assembler -
@@ -30,7 +29,7 @@ this_is_global:
 module_local:
 EOF
 
-echo '{ local: module_local; };' > $t/c.map
+echo '{ local: module_local; global: *; };' > $t/c.map
 
 ./mold -o $t/exe $t/a.o $t/b.o --version-script=$t/c.map
 
@@ -38,9 +37,9 @@ readelf --symbols $t/exe > $t/log
 
 grep -Eq '0 NOTYPE  LOCAL  DEFAULT .* local1' $t/log
 grep -Eq '0 NOTYPE  LOCAL  DEFAULT .* local2' $t/log
+grep -Eq '0 NOTYPE  LOCAL  DEFAULT .* module_local' $t/log
 grep -Eq '0 NOTYPE  GLOBAL DEFAULT .* foo' $t/log
 grep -Eq '0 NOTYPE  GLOBAL DEFAULT .* bar' $t/log
 grep -Eq '0 NOTYPE  GLOBAL DEFAULT .* this_is_global' $t/log
-grep -Eq '0 NOTYPE  GLOBAL DEFAULT .* module_local' $t/log
 
 echo OK
diff -pruN 1.3.1+dfsg-1/test/elf/synthetic-symbols.sh 1.4.0+dfsg-1/test/elf/synthetic-symbols.sh
--- 1.3.1+dfsg-1/test/elf/synthetic-symbols.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/synthetic-symbols.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
@@ -29,6 +28,7 @@ cat <<EOF | $CC -c -o $t/b.o -xc -
 
 extern char __ehdr_start[];
 extern char __executable_start[];
+extern char __dso_handle[];
 extern char _end[];
 extern char end[];
 extern char _etext[];
@@ -48,6 +48,7 @@ int main() {
 
   printf("__ehdr_start=%p\n", &__ehdr_start);
   printf("__executable_start=%p\n", &__executable_start);
+  printf("__dso_handle=%p\n", &__dso_handle);
   printf("%.*s\n", (int)(__stop_foo - __start_foo), __start_foo);
 }
 EOF
@@ -58,6 +59,7 @@ $QEMU $t/exe > $t/log
 
 grep -q '^__ehdr_start=0x40000$' $t/log
 grep -q '^__executable_start=0x40000$' $t/log
+grep -q '^__dso_handle=' $t/log
 grep -q '^section foo$' $t/log
 
 # Make sure that synthetic symbols overwrite existing ones
diff -pruN 1.3.1+dfsg-1/test/elf/sysroot2.sh 1.4.0+dfsg-1/test/elf/sysroot2.sh
--- 1.3.1+dfsg-1/test/elf/sysroot2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/sysroot2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 mkdir -p $t/sysroot/foo
diff -pruN 1.3.1+dfsg-1/test/elf/sysroot-linker-script.sh 1.4.0+dfsg-1/test/elf/sysroot-linker-script.sh
--- 1.3.1+dfsg-1/test/elf/sysroot-linker-script.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/sysroot-linker-script.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/sysroot.sh 1.4.0+dfsg-1/test/elf/sysroot.sh
--- 1.3.1+dfsg-1/test/elf/sysroot.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/sysroot.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/thin-archive.sh 1.4.0+dfsg-1/test/elf/thin-archive.sh
--- 1.3.1+dfsg-1/test/elf/thin-archive.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/thin-archive.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/long-long-long-filename.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/thread-count.sh 1.4.0+dfsg-1/test/elf/thread-count.sh
--- 1.3.1+dfsg-1/test/elf/thread-count.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/thread-count.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/tls-common.sh 1.4.0+dfsg-1/test/elf/tls-common.sh
--- 1.3.1+dfsg-1/test/elf/tls-common.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tls-common.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $GCC -o $t/a.o -c -xassembler -
diff -pruN 1.3.1+dfsg-1/test/elf/tlsdesc-import.sh 1.4.0+dfsg-1/test/elf/tlsdesc-import.sh
--- 1.3.1+dfsg-1/test/elf/tlsdesc-import.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tlsdesc-import.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 if [ $MACHINE = x86_64 ]; then
diff -pruN 1.3.1+dfsg-1/test/elf/tlsdesc.sh 1.4.0+dfsg-1/test/elf/tlsdesc.sh
--- 1.3.1+dfsg-1/test/elf/tlsdesc.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tlsdesc.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 if [ $MACHINE = x86_64 ]; then
diff -pruN 1.3.1+dfsg-1/test/elf/tlsdesc-static.sh 1.4.0+dfsg-1/test/elf/tlsdesc-static.sh
--- 1.3.1+dfsg-1/test/elf/tlsdesc-static.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tlsdesc-static.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 if [ $MACHINE = x86_64 ]; then
diff -pruN 1.3.1+dfsg-1/test/elf/tls-dso.sh 1.4.0+dfsg-1/test/elf/tls-dso.sh
--- 1.3.1+dfsg-1/test/elf/tls-dso.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tls-dso.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -shared -o $t/a.so -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/tls-gd2.sh 1.4.0+dfsg-1/test/elf/tls-gd2.sh
--- 1.3.1+dfsg-1/test/elf/tls-gd2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tls-gd2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 if [ $MACHINE = x86_64 ]; then
diff -pruN 1.3.1+dfsg-1/test/elf/tls-gd-mcmodel-large.sh 1.4.0+dfsg-1/test/elf/tls-gd-mcmodel-large.sh
--- 1.3.1+dfsg-1/test/elf/tls-gd-mcmodel-large.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tls-gd-mcmodel-large.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/tls-gd-noplt.sh 1.4.0+dfsg-1/test/elf/tls-gd-noplt.sh
--- 1.3.1+dfsg-1/test/elf/tls-gd-noplt.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tls-gd-noplt.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 if [ $MACHINE = x86_64 ]; then
diff -pruN 1.3.1+dfsg-1/test/elf/tls-gd.sh 1.4.0+dfsg-1/test/elf/tls-gd.sh
--- 1.3.1+dfsg-1/test/elf/tls-gd.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tls-gd.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 if [ $MACHINE = x86_64 ]; then
diff -pruN 1.3.1+dfsg-1/test/elf/tls-ie.sh 1.4.0+dfsg-1/test/elf/tls-ie.sh
--- 1.3.1+dfsg-1/test/elf/tls-ie.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tls-ie.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 if [ $MACHINE = x86_64 ]; then
diff -pruN 1.3.1+dfsg-1/test/elf/tls-large-tbss.sh 1.4.0+dfsg-1/test/elf/tls-large-tbss.sh
--- 1.3.1+dfsg-1/test/elf/tls-large-tbss.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tls-large-tbss.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/tls-ld-mcmodel-large.sh 1.4.0+dfsg-1/test/elf/tls-ld-mcmodel-large.sh
--- 1.3.1+dfsg-1/test/elf/tls-ld-mcmodel-large.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tls-ld-mcmodel-large.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/tls-ld-noplt.sh 1.4.0+dfsg-1/test/elf/tls-ld-noplt.sh
--- 1.3.1+dfsg-1/test/elf/tls-ld-noplt.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tls-ld-noplt.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 if [ $MACHINE = x86_64 ]; then
diff -pruN 1.3.1+dfsg-1/test/elf/tls-ld.sh 1.4.0+dfsg-1/test/elf/tls-ld.sh
--- 1.3.1+dfsg-1/test/elf/tls-ld.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tls-ld.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 if [ $MACHINE = x86_64 ]; then
diff -pruN 1.3.1+dfsg-1/test/elf/tls-le.sh 1.4.0+dfsg-1/test/elf/tls-le.sh
--- 1.3.1+dfsg-1/test/elf/tls-le.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tls-le.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 if [ $MACHINE = x86_64 ]; then
diff -pruN 1.3.1+dfsg-1/test/elf/tls-module-base.sh 1.4.0+dfsg-1/test/elf/tls-module-base.sh
--- 1.3.1+dfsg-1/test/elf/tls-module-base.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tls-module-base.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/tls-nopic.sh 1.4.0+dfsg-1/test/elf/tls-nopic.sh
--- 1.3.1+dfsg-1/test/elf/tls-nopic.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tls-nopic.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 if [ $MACHINE = x86_64 ]; then
diff -pruN 1.3.1+dfsg-1/test/elf/tls-pic.sh 1.4.0+dfsg-1/test/elf/tls-pic.sh
--- 1.3.1+dfsg-1/test/elf/tls-pic.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/tls-pic.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 if [ $MACHINE = x86_64 ]; then
diff -pruN 1.3.1+dfsg-1/test/elf/trace.sh 1.4.0+dfsg-1/test/elf/trace.sh
--- 1.3.1+dfsg-1/test/elf/trace.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/trace.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/trace-symbol.sh 1.4.0+dfsg-1/test/elf/trace-symbol.sh
--- 1.3.1+dfsg-1/test/elf/trace-symbol.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/trace-symbol.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/undefined.sh 1.4.0+dfsg-1/test/elf/undefined.sh
--- 1.3.1+dfsg-1/test/elf/undefined.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/undefined.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -x assembler -
diff -pruN 1.3.1+dfsg-1/test/elf/unique.sh 1.4.0+dfsg-1/test/elf/unique.sh
--- 1.3.1+dfsg-1/test/elf/unique.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/unique.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/unresolved-symbols.sh 1.4.0+dfsg-1/test/elf/unresolved-symbols.sh
--- 1.3.1+dfsg-1/test/elf/unresolved-symbols.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/unresolved-symbols.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/verbose.sh 1.4.0+dfsg-1/test/elf/verbose.sh
--- 1.3.1+dfsg-1/test/elf/verbose.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/verbose.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -xc -o $t/a.o -
diff -pruN 1.3.1+dfsg-1/test/elf/versioned-undef.sh 1.4.0+dfsg-1/test/elf/versioned-undef.sh
--- 1.3.1+dfsg-1/test/elf/versioned-undef.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/versioned-undef.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,14 +9,12 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # Skip if libc is musl because musl does not fully support GNU-style
 # symbol versioning.
-echo 'int main() {}' | $CC -o $t/exe -xc -
-readelf --dynamic $t/exe | grep -q ld-musl && { echo OK; exit; }
+ldd --help 2>&1 | grep -q musl && { echo skipped; exit; }
 
 cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
 int foo1() { return 1; }
diff -pruN 1.3.1+dfsg-1/test/elf/version-script10.sh 1.4.0+dfsg-1/test/elf/version-script10.sh
--- 1.3.1+dfsg-1/test/elf/version-script10.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script10.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 echo 'VER1 { foo[12]; }; VER2 {};' > $t/a.ver
diff -pruN 1.3.1+dfsg-1/test/elf/version-script11.sh 1.4.0+dfsg-1/test/elf/version-script11.sh
--- 1.3.1+dfsg-1/test/elf/version-script11.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script11.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<'EOF' > $t/a.ver
diff -pruN 1.3.1+dfsg-1/test/elf/version-script12.sh 1.4.0+dfsg-1/test/elf/version-script12.sh
--- 1.3.1+dfsg-1/test/elf/version-script12.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script12.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<'EOF' > $t/a.ver
diff -pruN 1.3.1+dfsg-1/test/elf/version-script13.sh 1.4.0+dfsg-1/test/elf/version-script13.sh
--- 1.3.1+dfsg-1/test/elf/version-script13.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script13.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<'EOF' > $t/a.ver
diff -pruN 1.3.1+dfsg-1/test/elf/version-script14.sh 1.4.0+dfsg-1/test/elf/version-script14.sh
--- 1.3.1+dfsg-1/test/elf/version-script14.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script14.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<'EOF' > $t/a.ver
diff -pruN 1.3.1+dfsg-1/test/elf/version-script15.sh 1.4.0+dfsg-1/test/elf/version-script15.sh
--- 1.3.1+dfsg-1/test/elf/version-script15.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script15.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<'EOF' > $t/a.ver
diff -pruN 1.3.1+dfsg-1/test/elf/version-script16.sh 1.4.0+dfsg-1/test/elf/version-script16.sh
--- 1.3.1+dfsg-1/test/elf/version-script16.sh	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script16.sh	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,26 @@
+#!/bin/bash
+export LC_ALL=C
+set -e
+CC="${TEST_CC:-cc}"
+CXX="${TEST_CXX:-c++}"
+GCC="${TEST_GCC:-gcc}"
+GXX="${TEST_GXX:-g++}"
+OBJDUMP="${OBJDUMP:-objdump}"
+MACHINE="${MACHINE:-$(uname -m)}"
+testname=$(basename "$0" .sh)
+echo -n "Testing $testname ... "
+t=out/test/elf/$MACHINE/$testname
+mkdir -p $t
+
+cat <<'EOF' > $t/a.ver
+{ local: *; global: extern "C++" { *foo*; }; };
+EOF
+
+cat <<EOF | $CC -fPIC -c -o $t/b.o -xc -
+void foobar() {}
+EOF
+
+$CC -B. -shared -Wl,--version-script=$t/a.ver -o $t/c.so $t/b.o
+readelf --dyn-syms $t/c.so | grep -q foobar
+
+echo OK
diff -pruN 1.3.1+dfsg-1/test/elf/version-script2.sh 1.4.0+dfsg-1/test/elf/version-script2.sh
--- 1.3.1+dfsg-1/test/elf/version-script2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF > $t/a.ver
diff -pruN 1.3.1+dfsg-1/test/elf/version-script3.sh 1.4.0+dfsg-1/test/elf/version-script3.sh
--- 1.3.1+dfsg-1/test/elf/version-script3.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script3.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF > $t/a.ver
diff -pruN 1.3.1+dfsg-1/test/elf/version-script4.sh 1.4.0+dfsg-1/test/elf/version-script4.sh
--- 1.3.1+dfsg-1/test/elf/version-script4.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script4.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF > $t/a.ver
diff -pruN 1.3.1+dfsg-1/test/elf/version-script5.sh 1.4.0+dfsg-1/test/elf/version-script5.sh
--- 1.3.1+dfsg-1/test/elf/version-script5.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script5.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF > $t/a.ver
diff -pruN 1.3.1+dfsg-1/test/elf/version-script6.sh 1.4.0+dfsg-1/test/elf/version-script6.sh
--- 1.3.1+dfsg-1/test/elf/version-script6.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script6.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<'EOF' > $t/a.ver
diff -pruN 1.3.1+dfsg-1/test/elf/version-script7.sh 1.4.0+dfsg-1/test/elf/version-script7.sh
--- 1.3.1+dfsg-1/test/elf/version-script7.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script7.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<'EOF' > $t/a.ver
diff -pruN 1.3.1+dfsg-1/test/elf/version-script8.sh 1.4.0+dfsg-1/test/elf/version-script8.sh
--- 1.3.1+dfsg-1/test/elf/version-script8.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script8.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF > $t/a.ver
diff -pruN 1.3.1+dfsg-1/test/elf/version-script9.sh 1.4.0+dfsg-1/test/elf/version-script9.sh
--- 1.3.1+dfsg-1/test/elf/version-script9.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script9.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 echo 'VER1 { extern "C++" {}; foo; }; VER2 {};' > $t/a.ver
diff -pruN 1.3.1+dfsg-1/test/elf/version-script.sh 1.4.0+dfsg-1/test/elf/version-script.sh
--- 1.3.1+dfsg-1/test/elf/version-script.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version-script.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 echo 'ver_x { global: *; };' > $t/a.ver
diff -pruN 1.3.1+dfsg-1/test/elf/version.sh 1.4.0+dfsg-1/test/elf/version.sh
--- 1.3.1+dfsg-1/test/elf/version.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/version.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 ./mold -v | grep -q 'mold .*compatible with GNU ld'
diff -pruN 1.3.1+dfsg-1/test/elf/visibility.sh 1.4.0+dfsg-1/test/elf/visibility.sh
--- 1.3.1+dfsg-1/test/elf/visibility.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/visibility.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -xc -c -o $t/a.o -
diff -pruN 1.3.1+dfsg-1/test/elf/warn-common.sh 1.4.0+dfsg-1/test/elf/warn-common.sh
--- 1.3.1+dfsg-1/test/elf/warn-common.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/warn-common.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fcommon -c -xc -o $t/a.o -
diff -pruN 1.3.1+dfsg-1/test/elf/warn-execstack.sh 1.4.0+dfsg-1/test/elf/warn-execstack.sh
--- 1.3.1+dfsg-1/test/elf/warn-execstack.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/warn-execstack.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/warn-once.sh 1.4.0+dfsg-1/test/elf/warn-once.sh
--- 1.3.1+dfsg-1/test/elf/warn-once.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/warn-once.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -fPIC -xc -o $t/a.o -
diff -pruN 1.3.1+dfsg-1/test/elf/warn-shared-textrel.sh 1.4.0+dfsg-1/test/elf/warn-shared-textrel.sh
--- 1.3.1+dfsg-1/test/elf/warn-shared-textrel.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/warn-shared-textrel.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,13 +9,11 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # Skip if libc is musl
-echo 'int main() {}' | $CC -o $t/exe -xc -
-readelf --dynamic $t/exe | grep -q ld-musl && { echo OK; exit; }
+ldd --help 2>&1 | grep -q musl && { echo skipped; exit; }
 
 # Skip if target is not x86-64
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/warn-textrel.sh 1.4.0+dfsg-1/test/elf/warn-textrel.sh
--- 1.3.1+dfsg-1/test/elf/warn-textrel.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/warn-textrel.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,13 +9,11 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # Skip if libc is musl
-echo 'int main() {}' | $CC -o $t/exe -xc -
-readelf --dynamic $t/exe | grep -q ld-musl && { echo OK; exit; }
+ldd --help 2>&1 | grep -q musl && { echo skipped; exit; }
 
 # Skip if target is not x86-64
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/warn-unresolved-symbols.sh 1.4.0+dfsg-1/test/elf/warn-unresolved-symbols.sh
--- 1.3.1+dfsg-1/test/elf/warn-unresolved-symbols.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/warn-unresolved-symbols.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/weak-export-dso.sh 1.4.0+dfsg-1/test/elf/weak-export-dso.sh
--- 1.3.1+dfsg-1/test/elf/weak-export-dso.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/weak-export-dso.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/weak-export-exe.sh 1.4.0+dfsg-1/test/elf/weak-export-exe.sh
--- 1.3.1+dfsg-1/test/elf/weak-export-exe.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/weak-export-exe.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/weak-undef.sh 1.4.0+dfsg-1/test/elf/weak-undef.sh
--- 1.3.1+dfsg-1/test/elf/weak-undef.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/weak-undef.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -fPIC -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/whole-archive.sh 1.4.0+dfsg-1/test/elf/whole-archive.sh
--- 1.3.1+dfsg-1/test/elf/whole-archive.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/whole-archive.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -x assembler -
diff -pruN 1.3.1+dfsg-1/test/elf/wrap.sh 1.4.0+dfsg-1/test/elf/wrap.sh
--- 1.3.1+dfsg-1/test/elf/wrap.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/wrap.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/z-cet-report.sh 1.4.0+dfsg-1/test/elf/z-cet-report.sh
--- 1.3.1+dfsg-1/test/elf/z-cet-report.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/z-cet-report.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -x assembler -
diff -pruN 1.3.1+dfsg-1/test/elf/z-defs.sh 1.4.0+dfsg-1/test/elf/z-defs.sh
--- 1.3.1+dfsg-1/test/elf/z-defs.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/z-defs.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/z-ibtplt.sh 1.4.0+dfsg-1/test/elf/z-ibtplt.sh
--- 1.3.1+dfsg-1/test/elf/z-ibtplt.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/z-ibtplt.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/z-ibt.sh 1.4.0+dfsg-1/test/elf/z-ibt.sh
--- 1.3.1+dfsg-1/test/elf/z-ibt.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/z-ibt.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/z-max-page-size.sh 1.4.0+dfsg-1/test/elf/z-max-page-size.sh
--- 1.3.1+dfsg-1/test/elf/z-max-page-size.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/z-max-page-size.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/z-nodefaultlib.sh 1.4.0+dfsg-1/test/elf/z-nodefaultlib.sh
--- 1.3.1+dfsg-1/test/elf/z-nodefaultlib.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/z-nodefaultlib.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/z-nodump.sh 1.4.0+dfsg-1/test/elf/z-nodump.sh
--- 1.3.1+dfsg-1/test/elf/z-nodump.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/z-nodump.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/z-now.sh 1.4.0+dfsg-1/test/elf/z-now.sh
--- 1.3.1+dfsg-1/test/elf/z-now.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/z-now.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/z-origin.sh 1.4.0+dfsg-1/test/elf/z-origin.sh
--- 1.3.1+dfsg-1/test/elf/z-origin.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/z-origin.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/elf/z-separate-code.sh 1.4.0+dfsg-1/test/elf/z-separate-code.sh
--- 1.3.1+dfsg-1/test/elf/z-separate-code.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/z-separate-code.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,13 +9,11 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # musl doesn't work with `-z noseparate-code`
-echo 'int main() {}' | $CC -o $t/exe -xc -
-readelf --dynamic $t/exe | grep -q ld-musl && { echo skipped; exit; }
+ldd --help 2>&1 | grep -q musl && { echo skipped; exit; }
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
 #include <stdio.h>
diff -pruN 1.3.1+dfsg-1/test/elf/z-shstk.sh 1.4.0+dfsg-1/test/elf/z-shstk.sh
--- 1.3.1+dfsg-1/test/elf/z-shstk.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/z-shstk.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/z-text.sh 1.4.0+dfsg-1/test/elf/z-text.sh
--- 1.3.1+dfsg-1/test/elf/z-text.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/z-text.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,13 +9,11 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 # Skip if libc is musl
-echo 'int main() {}' | $CC -o $t/exe -xc -
-readelf --dynamic $t/exe | grep -q ld-musl && { echo OK; exit; }
+ldd --help 2>&1 | grep -q musl && { echo skipped; exit; }
 
 # Skip if target is not x86-64
 [ $MACHINE = x86_64 ] || { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/elf/z-unknown.sh 1.4.0+dfsg-1/test/elf/z-unknown.sh
--- 1.3.1+dfsg-1/test/elf/z-unknown.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/elf/z-unknown.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/elf/$testname
+t=out/test/elf/$MACHINE/$testname
 mkdir -p $t
 
 ./mold -z no-such-opt 2>&1 | grep -q 'unknown command line option: -z no-such-opt'
diff -pruN 1.3.1+dfsg-1/test/gentoo-test.sh 1.4.0+dfsg-1/test/gentoo-test.sh
--- 1.3.1+dfsg-1/test/gentoo-test.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/gentoo-test.sh	2022-08-04 12:47:21.000000000 +0000
@@ -27,6 +27,7 @@ if ! docker image ls mold-gentoo | grep
 FROM gentoo/stage3
 RUN emerge-webrsync
 RUN echo 'USE="X ssl elogind -systemd corefonts truetype jpeg jpeg2k tiff zstd static-libs binary"' >> /etc/portage/make.conf && \
+    echo 'ACCEPT_KEYWORDS="~amd64"' >> /etc/portage/make.conf && \
     echo 'ACCEPT_LICENSE="* -@EULA"' >> /etc/portage/make.conf && \
     echo 'FEATURES="\${FEATURE} noclean nostrip ccache -ipc-sandbox -network-sandbox -pid-sandbox -sandbox"' >> /etc/portage/make.conf && \
     echo 'CCACHE_DIR="/ccache"' >> /etc/portage/make.conf
diff -pruN 1.3.1+dfsg-1/test/macho/add-ast-path.sh 1.4.0+dfsg-1/test/macho/add-ast-path.sh
--- 1.3.1+dfsg-1/test/macho/add-ast-path.sh	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/add-ast-path.sh	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,25 @@
+#!/bin/bash
+export LC_ALL=C
+set -e
+CC="${TEST_CC:-cc}"
+CXX="${TEST_CXX:-c++}"
+GCC="${TEST_GCC:-gcc}"
+GXX="${TEST_GXX:-g++}"
+OBJDUMP="${OBJDUMP:-objdump}"
+MACHINE="${MACHINE:-$(uname -m)}"
+testname=$(basename "$0" .sh)
+echo -n "Testing $testname ... "
+t=out/test/macho/$MACHINE/$testname
+mkdir -p $t
+
+cat <<EOF | $CC -o $t/a.o -c -xc -
+int main() {}
+EOF
+
+clang --ld-path=./ld64 -o $t/exe $t/a.o \
+  -Wl,-add_ast_path,foo -Wl,-add_ast_path,bar
+
+dsymutil -s $t/exe | grep -q 'N_AST.*foo'
+dsymutil -s $t/exe | grep -q 'N_AST.*bar'
+
+echo OK
diff -pruN 1.3.1+dfsg-1/test/macho/add-empty-section.sh 1.4.0+dfsg-1/test/macho/add-empty-section.sh
--- 1.3.1+dfsg-1/test/macho/add-empty-section.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/add-empty-section.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/adhoc-codesign.sh 1.4.0+dfsg-1/test/macho/adhoc-codesign.sh
--- 1.3.1+dfsg-1/test/macho/adhoc-codesign.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/adhoc-codesign.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/all-load.sh 1.4.0+dfsg-1/test/macho/all-load.sh
--- 1.3.1+dfsg-1/test/macho/all-load.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/all-load.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/application-extension.sh 1.4.0+dfsg-1/test/macho/application-extension.sh
--- 1.3.1+dfsg-1/test/macho/application-extension.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/application-extension.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<'EOF' > $t/a.tbd
diff -pruN 1.3.1+dfsg-1/test/macho/archive.sh 1.4.0+dfsg-1/test/macho/archive.sh
--- 1.3.1+dfsg-1/test/macho/archive.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/archive.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | clang -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/baserel.sh 1.4.0+dfsg-1/test/macho/baserel.sh
--- 1.3.1+dfsg-1/test/macho/baserel.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/baserel.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/basic.sh 1.4.0+dfsg-1/test/macho/basic.sh
--- 1.3.1+dfsg-1/test/macho/basic.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/basic.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/bss.sh 1.4.0+dfsg-1/test/macho/bss.sh
--- 1.3.1+dfsg-1/test/macho/bss.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/bss.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/bundle.sh 1.4.0+dfsg-1/test/macho/bundle.sh
--- 1.3.1+dfsg-1/test/macho/bundle.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/bundle.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/comdat.sh 1.4.0+dfsg-1/test/macho/comdat.sh
--- 1.3.1+dfsg-1/test/macho/comdat.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/comdat.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc++ -
diff -pruN 1.3.1+dfsg-1/test/macho/common-alignment.sh 1.4.0+dfsg-1/test/macho/common-alignment.sh
--- 1.3.1+dfsg-1/test/macho/common-alignment.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/common-alignment.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -fcommon -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/common.sh 1.4.0+dfsg-1/test/macho/common.sh
--- 1.3.1+dfsg-1/test/macho/common.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/common.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -fcommon -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/cstring.sh 1.4.0+dfsg-1/test/macho/cstring.sh
--- 1.3.1+dfsg-1/test/macho/cstring.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/cstring.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/data-reloc.sh 1.4.0+dfsg-1/test/macho/data-reloc.sh
--- 1.3.1+dfsg-1/test/macho/data-reloc.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/data-reloc.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc - -fPIC
diff -pruN 1.3.1+dfsg-1/test/macho/dead-strip-dylibs2.sh 1.4.0+dfsg-1/test/macho/dead-strip-dylibs2.sh
--- 1.3.1+dfsg-1/test/macho/dead-strip-dylibs2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/dead-strip-dylibs2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 mkdir -p $t/Foo.framework
diff -pruN 1.3.1+dfsg-1/test/macho/dead-strip-dylibs.sh 1.4.0+dfsg-1/test/macho/dead-strip-dylibs.sh
--- 1.3.1+dfsg-1/test/macho/dead-strip-dylibs.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/dead-strip-dylibs.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/libfoo.dylib -shared -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/dead-strip.sh 1.4.0+dfsg-1/test/macho/dead-strip.sh
--- 1.3.1+dfsg-1/test/macho/dead-strip.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/dead-strip.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/dependency-info.sh 1.4.0+dfsg-1/test/macho/dependency-info.sh
--- 1.3.1+dfsg-1/test/macho/dependency-info.sh	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/dependency-info.sh	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,25 @@
+#!/bin/bash
+export LC_ALL=C
+set -e
+CC="${TEST_CC:-cc}"
+CXX="${TEST_CXX:-c++}"
+GCC="${TEST_GCC:-gcc}"
+GXX="${TEST_GXX:-g++}"
+OBJDUMP="${OBJDUMP:-objdump}"
+MACHINE="${MACHINE:-$(uname -m)}"
+testname=$(basename "$0" .sh)
+echo -n "Testing $testname ... "
+t=out/test/macho/$MACHINE/$testname
+mkdir -p $t
+
+cat <<EOF | $CC -o $t/a.o -c -xc -
+int main() {}
+EOF
+
+clang --ld-path=./ld64 -o $t/exe $t/a.o -Wl,-dependency_info,$t/dep
+
+grep -q mold $t/dep
+grep -q "\x10$t/a.o" $t/dep
+grep -q "@$t/exe" $t/dep
+
+echo OK
diff -pruN 1.3.1+dfsg-1/test/macho/dlinfo.sh 1.4.0+dfsg-1/test/macho/dlinfo.sh
--- 1.3.1+dfsg-1/test/macho/dlinfo.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/dlinfo.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | clang -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/duplicate-error.sh 1.4.0+dfsg-1/test/macho/duplicate-error.sh
--- 1.3.1+dfsg-1/test/macho/duplicate-error.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/duplicate-error.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/dylib.sh 1.4.0+dfsg-1/test/macho/dylib.sh
--- 1.3.1+dfsg-1/test/macho/dylib.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/dylib.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/entry.sh 1.4.0+dfsg-1/test/macho/entry.sh
--- 1.3.1+dfsg-1/test/macho/entry.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/entry.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/exception.sh 1.4.0+dfsg-1/test/macho/exception.sh
--- 1.3.1+dfsg-1/test/macho/exception.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/exception.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | clang++ -c -o $t/a.o -xc++ -
diff -pruN 1.3.1+dfsg-1/test/macho/export-dynamic.sh 1.4.0+dfsg-1/test/macho/export-dynamic.sh
--- 1.3.1+dfsg-1/test/macho/export-dynamic.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/export-dynamic.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc - -flto
diff -pruN 1.3.1+dfsg-1/test/macho/exported-symbols-list.sh 1.4.0+dfsg-1/test/macho/exported-symbols-list.sh
--- 1.3.1+dfsg-1/test/macho/exported-symbols-list.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/exported-symbols-list.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/filepath2.sh 1.4.0+dfsg-1/test/macho/filepath2.sh
--- 1.3.1+dfsg-1/test/macho/filepath2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/filepath2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/filepath.sh 1.4.0+dfsg-1/test/macho/filepath.sh
--- 1.3.1+dfsg-1/test/macho/filepath.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/filepath.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/force-load.sh 1.4.0+dfsg-1/test/macho/force-load.sh
--- 1.3.1+dfsg-1/test/macho/force-load.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/force-load.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/framework.sh 1.4.0+dfsg-1/test/macho/framework.sh
--- 1.3.1+dfsg-1/test/macho/framework.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/framework.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 mkdir -p $t/Foo.framework
diff -pruN 1.3.1+dfsg-1/test/macho/headerpad-max-install-names.sh 1.4.0+dfsg-1/test/macho/headerpad-max-install-names.sh
--- 1.3.1+dfsg-1/test/macho/headerpad-max-install-names.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/headerpad-max-install-names.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/headerpad.sh 1.4.0+dfsg-1/test/macho/headerpad.sh
--- 1.3.1+dfsg-1/test/macho/headerpad.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/headerpad.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 [ "`uname -p`" = arm ] && { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/macho/hello2.sh 1.4.0+dfsg-1/test/macho/hello2.sh
--- 1.3.1+dfsg-1/test/macho/hello2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/hello2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/hello3.sh 1.4.0+dfsg-1/test/macho/hello3.sh
--- 1.3.1+dfsg-1/test/macho/hello3.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/hello3.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/hello4.sh 1.4.0+dfsg-1/test/macho/hello4.sh
--- 1.3.1+dfsg-1/test/macho/hello4.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/hello4.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/hello5.sh 1.4.0+dfsg-1/test/macho/hello5.sh
--- 1.3.1+dfsg-1/test/macho/hello5.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/hello5.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/hello.sh 1.4.0+dfsg-1/test/macho/hello.sh
--- 1.3.1+dfsg-1/test/macho/hello.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/hello.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/hidden-l.sh 1.4.0+dfsg-1/test/macho/hidden-l.sh
--- 1.3.1+dfsg-1/test/macho/hidden-l.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/hidden-l.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -fPIC -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/install-name.sh 1.4.0+dfsg-1/test/macho/install-name.sh
--- 1.3.1+dfsg-1/test/macho/install-name.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/install-name.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/lc-build-version.sh 1.4.0+dfsg-1/test/macho/lc-build-version.sh
--- 1.3.1+dfsg-1/test/macho/lc-build-version.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/lc-build-version.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
@@ -18,6 +17,6 @@ int main() {}
 EOF
 
 clang --ld-path=./ld64 -o $t/exe $t/a.o
-otool -l $t/exe | grep -q 'tool 1836018788'
+otool -l $t/exe | grep -q 'tool 54321'
 
 echo OK
diff -pruN 1.3.1+dfsg-1/test/macho/lc-linker-option.sh 1.4.0+dfsg-1/test/macho/lc-linker-option.sh
--- 1.3.1+dfsg-1/test/macho/lc-linker-option.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/lc-linker-option.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc - -fmodules
diff -pruN 1.3.1+dfsg-1/test/macho/lib1.sh 1.4.0+dfsg-1/test/macho/lib1.sh
--- 1.3.1+dfsg-1/test/macho/lib1.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/lib1.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/libfoo.dylib -shared -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/libunwind.sh 1.4.0+dfsg-1/test/macho/libunwind.sh
--- 1.3.1+dfsg-1/test/macho/libunwind.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/libunwind.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | clang++ -c -o $t/a.o -xobjective-c++ -
diff -pruN 1.3.1+dfsg-1/test/macho/linker-optimization-hints.sh 1.4.0+dfsg-1/test/macho/linker-optimization-hints.sh
--- 1.3.1+dfsg-1/test/macho/linker-optimization-hints.sh	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/linker-optimization-hints.sh	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,51 @@
+#!/bin/bash
+export LC_ALL=C
+set -e
+CC="${TEST_CC:-cc}"
+CXX="${TEST_CXX:-c++}"
+GCC="${TEST_GCC:-gcc}"
+GXX="${TEST_GXX:-g++}"
+OBJDUMP="${OBJDUMP:-objdump}"
+MACHINE="${MACHINE:-$(uname -m)}"
+testname=$(basename "$0" .sh)
+echo -n "Testing $testname ... "
+t=out/test/macho/$MACHINE/$testname
+mkdir -p $t
+
+cat <<EOF | $CC -o $t/a.o -c -xc - -O2
+#include <stdio.h>
+
+char x1 = -1;
+short x2 = -1;
+int x3 = -1;
+long x4 = -1;
+int x5[] = {0, 1, 2, 3};
+long x6[] = {0, 1, 2, 3};
+
+void hello() {
+  printf("Hello world ");
+}
+EOF
+
+cat <<EOF | $CC -o $t/b.o -c -xc - -O2
+#include <stdio.h>
+
+void hello();
+
+extern char x1;
+extern short x2;
+extern int x3;
+extern long x4;
+extern int x5[];
+extern long x6[];
+
+int main() {
+  hello();
+  printf("%d %d %d %ld %d %ld\n", x1, x2, x3, x4, x5[2], x6[3]);
+}
+EOF
+
+clang --ld-path=./ld64 -o $t/exe $t/a.o $t/b.o
+$t/exe | grep -q 'Hello world -1 -1 -1 -1 2 3'
+
+echo OK
diff -pruN 1.3.1+dfsg-1/test/macho/lto.sh 1.4.0+dfsg-1/test/macho/lto.sh
--- 1.3.1+dfsg-1/test/macho/lto.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/lto.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc - -flto
diff -pruN 1.3.1+dfsg-1/test/macho/macos-version-min.sh 1.4.0+dfsg-1/test/macho/macos-version-min.sh
--- 1.3.1+dfsg-1/test/macho/macos-version-min.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/macos-version-min.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/map.sh 1.4.0+dfsg-1/test/macho/map.sh
--- 1.3.1+dfsg-1/test/macho/map.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/map.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/merge-scope.sh 1.4.0+dfsg-1/test/macho/merge-scope.sh
--- 1.3.1+dfsg-1/test/macho/merge-scope.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/merge-scope.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xassembler -
diff -pruN 1.3.1+dfsg-1/test/macho/missing-error.sh 1.4.0+dfsg-1/test/macho/missing-error.sh
--- 1.3.1+dfsg-1/test/macho/missing-error.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/missing-error.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/needed-framework.sh 1.4.0+dfsg-1/test/macho/needed-framework.sh
--- 1.3.1+dfsg-1/test/macho/needed-framework.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/needed-framework.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 mkdir -p $t/Foo.framework
diff -pruN 1.3.1+dfsg-1/test/macho/needed-l.sh 1.4.0+dfsg-1/test/macho/needed-l.sh
--- 1.3.1+dfsg-1/test/macho/needed-l.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/needed-l.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/libfoo.dylib -shared -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/no-function-starts.sh 1.4.0+dfsg-1/test/macho/no-function-starts.sh
--- 1.3.1+dfsg-1/test/macho/no-function-starts.sh	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/no-function-starts.sh	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,26 @@
+#!/bin/bash
+export LC_ALL=C
+set -e
+CC="${TEST_CC:-cc}"
+CXX="${TEST_CXX:-c++}"
+GCC="${TEST_GCC:-gcc}"
+GXX="${TEST_GXX:-g++}"
+OBJDUMP="${OBJDUMP:-objdump}"
+MACHINE="${MACHINE:-$(uname -m)}"
+testname=$(basename "$0" .sh)
+echo -n "Testing $testname ... "
+t=out/test/macho/$MACHINE/$testname
+mkdir -p $t
+
+cat <<EOF | $CC -o $t/a.o -c -xc -
+int main() {}
+EOF
+
+clang --ld-path=./ld64 -o $t/exe1 $t/a.o
+otool -l $t/exe1 | grep -q LC_FUNCTION_STARTS
+
+clang --ld-path=./ld64 -o $t/exe2 $t/a.o -Wl,-no_function_starts
+otool -l $t/exe2 > $t/log
+! grep -q LC_FUNCTION_STARTS $t/log || false
+
+echo OK
diff -pruN 1.3.1+dfsg-1/test/macho/objc-selector.sh 1.4.0+dfsg-1/test/macho/objc-selector.sh
--- 1.3.1+dfsg-1/test/macho/objc-selector.sh	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/objc-selector.sh	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,26 @@
+#!/bin/bash
+export LC_ALL=C
+set -e
+CC="${TEST_CC:-cc}"
+CXX="${TEST_CXX:-c++}"
+GCC="${TEST_GCC:-gcc}"
+GXX="${TEST_GXX:-g++}"
+OBJDUMP="${OBJDUMP:-objdump}"
+MACHINE="${MACHINE:-$(uname -m)}"
+testname=$(basename "$0" .sh)
+echo -n "Testing $testname ... "
+t=out/test/macho/$MACHINE/$testname
+mkdir -p $t
+
+cat <<EOF | $CC -o $t/a.o -c -xobjective-c -
+#import <Foundation/Foundation.h>
+int main() {
+  NSProcessInfo *info = [NSProcessInfo processInfo];
+  NSLog(@"processName: %@", [info processName]);
+}
+EOF
+
+clang --ld-path=./ld64 -o $t/exe $t/a.o -framework foundation -Wl,-ObjC
+$t/exe 2>&1 | fgrep -q 'processName: exe'
+
+echo OK
diff -pruN 1.3.1+dfsg-1/test/macho/objc.sh 1.4.0+dfsg-1/test/macho/objc.sh
--- 1.3.1+dfsg-1/test/macho/objc.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/objc.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xobjective-c -
diff -pruN 1.3.1+dfsg-1/test/macho/object-path-lto.sh 1.4.0+dfsg-1/test/macho/object-path-lto.sh
--- 1.3.1+dfsg-1/test/macho/object-path-lto.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/object-path-lto.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc - -flto
diff -pruN 1.3.1+dfsg-1/test/macho/order-file.sh 1.4.0+dfsg-1/test/macho/order-file.sh
--- 1.3.1+dfsg-1/test/macho/order-file.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/order-file.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/pagezero-size2.sh 1.4.0+dfsg-1/test/macho/pagezero-size2.sh
--- 1.3.1+dfsg-1/test/macho/pagezero-size2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/pagezero-size2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/pagezero-size3.sh 1.4.0+dfsg-1/test/macho/pagezero-size3.sh
--- 1.3.1+dfsg-1/test/macho/pagezero-size3.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/pagezero-size3.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/pagezero-size.sh 1.4.0+dfsg-1/test/macho/pagezero-size.sh
--- 1.3.1+dfsg-1/test/macho/pagezero-size.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/pagezero-size.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 [ "`uname -p`" = arm ] && { echo skipped; exit; }
diff -pruN 1.3.1+dfsg-1/test/macho/platform-version.sh 1.4.0+dfsg-1/test/macho/platform-version.sh
--- 1.3.1+dfsg-1/test/macho/platform-version.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/platform-version.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/private-extern.sh 1.4.0+dfsg-1/test/macho/private-extern.sh
--- 1.3.1+dfsg-1/test/macho/private-extern.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/private-extern.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/reexport-l.sh 1.4.0+dfsg-1/test/macho/reexport-l.sh
--- 1.3.1+dfsg-1/test/macho/reexport-l.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/reexport-l.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/reproducibility.sh 1.4.0+dfsg-1/test/macho/reproducibility.sh
--- 1.3.1+dfsg-1/test/macho/reproducibility.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/reproducibility.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/response-file.sh 1.4.0+dfsg-1/test/macho/response-file.sh
--- 1.3.1+dfsg-1/test/macho/response-file.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/response-file.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 echo ' -help' > $t/rsp
diff -pruN 1.3.1+dfsg-1/test/macho/rpath.sh 1.4.0+dfsg-1/test/macho/rpath.sh
--- 1.3.1+dfsg-1/test/macho/rpath.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/rpath.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/search-dylibs-first.sh 1.4.0+dfsg-1/test/macho/search-dylibs-first.sh
--- 1.3.1+dfsg-1/test/macho/search-dylibs-first.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/search-dylibs-first.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/search-paths-first.sh 1.4.0+dfsg-1/test/macho/search-paths-first.sh
--- 1.3.1+dfsg-1/test/macho/search-paths-first.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/search-paths-first.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/sectcreate.sh 1.4.0+dfsg-1/test/macho/sectcreate.sh
--- 1.3.1+dfsg-1/test/macho/sectcreate.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/sectcreate.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/stack-size.sh 1.4.0+dfsg-1/test/macho/stack-size.sh
--- 1.3.1+dfsg-1/test/macho/stack-size.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/stack-size.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/start-stop-symbol.sh 1.4.0+dfsg-1/test/macho/start-stop-symbol.sh
--- 1.3.1+dfsg-1/test/macho/start-stop-symbol.sh	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/start-stop-symbol.sh	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,39 @@
+#!/bin/bash
+export LC_ALL=C
+set -e
+CC="${TEST_CC:-cc}"
+CXX="${TEST_CXX:-c++}"
+GCC="${TEST_GCC:-gcc}"
+GXX="${TEST_GXX:-g++}"
+OBJDUMP="${OBJDUMP:-objdump}"
+MACHINE="${MACHINE:-$(uname -m)}"
+testname=$(basename "$0" .sh)
+echo -n "Testing $testname ... "
+t=out/test/macho/$testname
+mkdir -p $t
+
+cat <<'EOF' | $CC -o $t/a.o -c -xc -
+#include <stdio.h>
+#include <stdint.h>
+
+extern char a __asm("section$start$__TEXT$__text");
+extern char b __asm("section$end$__TEXT$__text");
+
+extern char c __asm("section$start$__TEXT$__foo");
+extern char d __asm("section$end$__TEXT$__foo");
+
+extern char e __asm("section$start$__FOO$__foo");
+extern char f __asm("section$end$__FOO$__foo");
+
+extern char g __asm("segment$start$__TEXT");
+extern char h __asm("segment$end$__TEXT");
+
+int main() {
+  printf("%p %p %p %p %p %p %p %p\n", &a, &b, &c, &d, &e, &f, &g, &h);
+}
+EOF
+
+clang --ld-path=./ld64 -o $t/exe $t/a.o
+$t/exe > /dev/null
+
+echo OK
diff -pruN 1.3.1+dfsg-1/test/macho/subsections-via-symbols.sh 1.4.0+dfsg-1/test/macho/subsections-via-symbols.sh
--- 1.3.1+dfsg-1/test/macho/subsections-via-symbols.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/subsections-via-symbols.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xassembler -
diff -pruN 1.3.1+dfsg-1/test/macho/syslibroot.sh 1.4.0+dfsg-1/test/macho/syslibroot.sh
--- 1.3.1+dfsg-1/test/macho/syslibroot.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/syslibroot.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 mkdir -p $t/foo/bar
diff -pruN 1.3.1+dfsg-1/test/macho/tbd-add.sh 1.4.0+dfsg-1/test/macho/tbd-add.sh
--- 1.3.1+dfsg-1/test/macho/tbd-add.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/tbd-add.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat > $t/libfoo.tbd <<'EOF'
diff -pruN 1.3.1+dfsg-1/test/macho/tbd-hide.sh 1.4.0+dfsg-1/test/macho/tbd-hide.sh
--- 1.3.1+dfsg-1/test/macho/tbd-hide.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/tbd-hide.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat > $t/libfoo.tbd <<'EOF'
diff -pruN 1.3.1+dfsg-1/test/macho/tbd-install-name.sh 1.4.0+dfsg-1/test/macho/tbd-install-name.sh
--- 1.3.1+dfsg-1/test/macho/tbd-install-name.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/tbd-install-name.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat > $t/libfoo.tbd <<'EOF'
diff -pruN 1.3.1+dfsg-1/test/macho/tbd-previous.sh 1.4.0+dfsg-1/test/macho/tbd-previous.sh
--- 1.3.1+dfsg-1/test/macho/tbd-previous.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/tbd-previous.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat > $t/libfoo.tbd <<'EOF'
diff -pruN 1.3.1+dfsg-1/test/macho/tbd-reexport.sh 1.4.0+dfsg-1/test/macho/tbd-reexport.sh
--- 1.3.1+dfsg-1/test/macho/tbd-reexport.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/tbd-reexport.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 mkdir -p $t/libs/SomeFramework.framework/
diff -pruN 1.3.1+dfsg-1/test/macho/tbd.sh 1.4.0+dfsg-1/test/macho/tbd.sh
--- 1.3.1+dfsg-1/test/macho/tbd.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/tbd.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 mkdir -p $t/libs/SomeFramework.framework/
diff -pruN 1.3.1+dfsg-1/test/macho/tls2.sh 1.4.0+dfsg-1/test/macho/tls2.sh
--- 1.3.1+dfsg-1/test/macho/tls2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/tls2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 # For some reason, this test fails only on GitHub CI.
diff -pruN 1.3.1+dfsg-1/test/macho/tls.sh 1.4.0+dfsg-1/test/macho/tls.sh
--- 1.3.1+dfsg-1/test/macho/tls.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/tls.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -shared -o $t/a.dylib -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/undef.sh 1.4.0+dfsg-1/test/macho/undef.sh
--- 1.3.1+dfsg-1/test/macho/undef.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/undef.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/unexported-symbols-list.sh 1.4.0+dfsg-1/test/macho/unexported-symbols-list.sh
--- 1.3.1+dfsg-1/test/macho/unexported-symbols-list.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/unexported-symbols-list.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/universal.sh 1.4.0+dfsg-1/test/macho/universal.sh
--- 1.3.1+dfsg-1/test/macho/universal.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/universal.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/unkown-tbd-target.sh 1.4.0+dfsg-1/test/macho/unkown-tbd-target.sh
--- 1.3.1+dfsg-1/test/macho/unkown-tbd-target.sh	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/unkown-tbd-target.sh	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,42 @@
+#!/bin/bash
+export LC_ALL=C
+set -e
+CC="${TEST_CC:-cc}"
+CXX="${TEST_CXX:-c++}"
+GCC="${TEST_GCC:-gcc}"
+GXX="${TEST_GXX:-g++}"
+OBJDUMP="${OBJDUMP:-objdump}"
+MACHINE="${MACHINE:-$(uname -m)}"
+testname=$(basename "$0" .sh)
+echo -n "Testing $testname ... "
+t=out/test/macho/$MACHINE/$testname
+mkdir -p $t
+
+cat <<EOF | clang -o $t/a.o -c -xc -
+int foo();
+int main() { foo(); }
+EOF
+
+cat > $t/b.tbd <<EOF
+--- !tapi-tbd
+tbd-version:     4
+targets:         [ x86_64-bar, arm64-bar ]
+uuids:
+  - target:          x86_64-bar
+    value:           00000000-0000-0000-0000-000000000000
+  - target:          arm64-bar
+    value:           00000000-0000-0000-0000-000000000000
+install-name:    '/usr/lib/bar'
+current-version: 0
+compatibility-version: 0
+exports:
+  - targets:         [ x86_64-bar ]
+    symbols:         [ _foo ]
+  - targets:         [ arm64-bar ]
+    symbols:         [ _foo ]
+...
+EOF
+
+clang --ld-path=./ld64 -o $t/exe $t/a.o $t/b.tbd
+
+echo OK
diff -pruN 1.3.1+dfsg-1/test/macho/U.sh 1.4.0+dfsg-1/test/macho/U.sh
--- 1.3.1+dfsg-1/test/macho/U.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/U.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/uuid2.sh 1.4.0+dfsg-1/test/macho/uuid2.sh
--- 1.3.1+dfsg-1/test/macho/uuid2.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/uuid2.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/uuid.sh 1.4.0+dfsg-1/test/macho/uuid.sh
--- 1.3.1+dfsg-1/test/macho/uuid.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/uuid.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/weak-def-dylib.sh 1.4.0+dfsg-1/test/macho/weak-def-dylib.sh
--- 1.3.1+dfsg-1/test/macho/weak-def-dylib.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/weak-def-dylib.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/weak-def-ref.sh 1.4.0+dfsg-1/test/macho/weak-def-ref.sh
--- 1.3.1+dfsg-1/test/macho/weak-def-ref.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/weak-def-ref.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CXX -o $t/a.o -c -xc++ -
diff -pruN 1.3.1+dfsg-1/test/macho/weak-def.sh 1.4.0+dfsg-1/test/macho/weak-def.sh
--- 1.3.1+dfsg-1/test/macho/weak-def.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/weak-def.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/weak-l.sh 1.4.0+dfsg-1/test/macho/weak-l.sh
--- 1.3.1+dfsg-1/test/macho/weak-l.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/weak-l.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/libfoo.dylib -shared -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/weak-undef.sh 1.4.0+dfsg-1/test/macho/weak-undef.sh
--- 1.3.1+dfsg-1/test/macho/weak-undef.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/weak-undef.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -c -o $t/a.o -xc -
diff -pruN 1.3.1+dfsg-1/test/macho/Z.sh 1.4.0+dfsg-1/test/macho/Z.sh
--- 1.3.1+dfsg-1/test/macho/Z.sh	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/macho/Z.sh	2022-08-04 12:47:21.000000000 +0000
@@ -9,8 +9,7 @@ OBJDUMP="${OBJDUMP:-objdump}"
 MACHINE="${MACHINE:-$(uname -m)}"
 testname=$(basename "$0" .sh)
 echo -n "Testing $testname ... "
-cd "$(dirname "$0")"/../..
-t=out/test/macho/$testname
+t=out/test/macho/$MACHINE/$testname
 mkdir -p $t
 
 cat <<EOF | $CC -o $t/a.o -c -xc -
diff -pruN 1.3.1+dfsg-1/test/Makefile.darwin 1.4.0+dfsg-1/test/Makefile.darwin
--- 1.3.1+dfsg-1/test/Makefile.darwin	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/Makefile.darwin	1970-01-01 00:00:00.000000000 +0000
@@ -1,15 +0,0 @@
-TESTS = $(wildcard macho/*.sh)
-
-test: $(TESTS)
-
-# macOS's GNU make hasn't been updated since 3.8.1 perhaps due a concern
-# of GPLv3. The --output-sync flag was introduced in GNU Make 4.0, so we
-# can't use that flag on macOS.
-#
-# `tail -r | tail -r` is a poor-man's way to enable full buffering on a
-# command output. `tail -r` outputs an input from the last line to the
-# first.
-$(TESTS):
-	@set -o pipefail; ./$@ 2>&1 | tail -r | tail -r
-
-.PHONY: test $(TESTS)
diff -pruN 1.3.1+dfsg-1/test/Makefile.linux 1.4.0+dfsg-1/test/Makefile.linux
--- 1.3.1+dfsg-1/test/Makefile.linux	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/test/Makefile.linux	1970-01-01 00:00:00.000000000 +0000
@@ -1,8 +0,0 @@
-TESTS = $(wildcard elf/*.sh)
-
-test: $(TESTS)
-
-$(TESTS):
-	@./$@
-
-.PHONY: test $(TESTS)
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/Cargo.lock 1.4.0+dfsg-1/third-party/rust-demangle/Cargo.lock
--- 1.3.1+dfsg-1/third-party/rust-demangle/Cargo.lock	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/Cargo.lock	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,23 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "cc"
+version = "1.0.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+
+[[package]]
+name = "rust-demangle-c-test-harness"
+version = "0.0.0"
+dependencies = [
+ "cc",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/Cargo.toml 1.4.0+dfsg-1/third-party/rust-demangle/Cargo.toml
--- 1.3.1+dfsg-1/third-party/rust-demangle/Cargo.toml	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/Cargo.toml	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,3 @@
+[workspace]
+members = ["test-harness"]
+default-members = ["test-harness"]
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/.clang-format 1.4.0+dfsg-1/third-party/rust-demangle/.clang-format
--- 1.3.1+dfsg-1/third-party/rust-demangle/.clang-format	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/.clang-format	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,4 @@
+# Attempt to mimic the Rust official style.
+BasedOnStyle: LLVM
+IndentWidth: 4
+AlignAfterOpenBracket: BlockIndent
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/.gitignore 1.4.0+dfsg-1/third-party/rust-demangle/.gitignore
--- 1.3.1+dfsg-1/third-party/rust-demangle/.gitignore	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/.gitignore	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1 @@
+target/
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/LICENSE-APACHE 1.4.0+dfsg-1/third-party/rust-demangle/LICENSE-APACHE
--- 1.3.1+dfsg-1/third-party/rust-demangle/LICENSE-APACHE	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/LICENSE-APACHE	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,176 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/LICENSE-MIT 1.4.0+dfsg-1/third-party/rust-demangle/LICENSE-MIT
--- 1.3.1+dfsg-1/third-party/rust-demangle/LICENSE-MIT	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/LICENSE-MIT	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/README.md 1.4.0+dfsg-1/third-party/rust-demangle/README.md
--- 1.3.1+dfsg-1/third-party/rust-demangle/README.md	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/README.md	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,161 @@
+# `rust-demangle.c`
+
+This is a single-file C99 port of the official Rust symbol demangler ([`rustc-demangle`](https://github.com/rust-lang/rustc-demangle), a Rust library).
+
+## Usecases
+
+This C port is intended for situations in which a Rust dependency is hard to
+justify, or effectively impossible (e.g. platform toolchains, that would be used
+*while building Rust*, not the other way around).
+
+If a Rust dependency is acceptable, [using `rustc-demangle` from C](https://github.com/rust-lang/rustc-demangle#usage-from-non-rust-languages)
+(or other languages via FFI) is possible, and may be preferred over this C port.
+
+## Status
+
+As this C port was originally (see the [History](#history) section) *only* for the
+`rustc-demangle` code demangling the (new at the time) [Rust RFC2603 (aka "`v0`") mangling scheme](https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html),
+it may lag behind `rustc-demangle` in functionality, for now.
+
+The current port status by category is:
+* **(UNPORTED)** `legacy` (pre-RFC2603 Rust symbols) demangling
+* `v0` ([RFC2603](https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html) Rust symbols) demangling
+  * **ported** PRs:
+    * [[#23] Support demangling the new Rust mangling scheme (v0).](https://github.com/rust-lang/rustc-demangle/pull/23)
+    * [[#26] v0: allow identifiers to start with a digit.](https://github.com/rust-lang/rustc-demangle/pull/26)
+    * [[#53] v0: replace `skip_*` methods with `print_*` methods in a "skip printing" mode.](https://github.com/rust-lang/rustc-demangle/pull/53)
+      * arguably backported to Rust, as the C port always took this approach
+  * **(UNPORTED)** symbol prefix flexibility (`__R` and `R`, instead of `_R`)
+  * **(UNPORTED)** `min_const_generics` constants (`bool`, `char`, negative signed integers)
+    * this arguably also includes `p` as an *untyped* placeholder constant
+  * **(UNPORTED)** [`str` and structural constants](https://github.com/rust-lang/rfcs/pull/3161)
+    (only usable in `const` generics on unstable Rust)
+  * **(UNPORTED)** recursion limits
+* miscellaneous
+  * **(UNPORTED)** extraneous symbol suffix (e.g. `.llvm.*`) removal
+  * **(UNPORTED)** output size limits
+
+Notable differences (intentionally) introduced by porting:
+* `rustc-demangle` can't use the heap (as it's `#![no_std]`), but the C port does
+  * this is mainly dictated by the ergonomics of the `rust_demangle` API, which
+    requires `malloc`/`realloc` to return a new C string allocation
+  * if there is demand for it, `rust_demangle` support could be made optional,
+    forcing heap-less users to always use `rust_demangle_with_callback` instead
+  * a subtler consequence is that `rustc-demangle` uses a fixed-size buffer on
+    the stack for punycode decoding, while the C port can allocate it on the heap
+* Unicode support is always handrolled in the C port, and often simplified
+
+## Usage
+
+Get `rust-demangle.c` and `rust-demangle.h` (via `git submodule`, vendoring, etc.),
+add them to your project's build system (as C source, and include path, respectively),
+then you can call `rust_demangle` with a symbol and some flags, e.g.:
+```c
+#include <rust-demangle.h>
+#include <stdio.h>
+
+int main() {
+    const char *sym = "_RNvNtCsbmNqQUJIY6D_4core3foo3bar";
+
+    printf("demangle(%s) = %s\n", sym, rust_demangle(sym, 0));
+
+    printf(
+        "demangle(%s, VERBOSE) = %s\n", sym,
+        rust_demangle(sym, RUST_DEMANGLE_FLAG_VERBOSE)
+    );
+}
+```
+which prints out, when ran:
+```
+demangle(_RNvNtCsbmNqQUJIY6D_4core3foo3bar) = core::foo::bar
+demangle(_RNvNtCsbmNqQUJIY6D_4core3foo3bar, VERBOSE) = core[846817f741e54dfd]::foo::bar
+```
+
+Note that the example leaks the returned C strings, ideally you would `free` them.
+
+### Advanced usage (callback-based API)
+
+If you want to avoid the cost of allocating the output in memory, you can also
+use `rust_demangle_with_callback`, which takes a "printing" callback instead, e.g.:
+```c
+#include <rust-demangle.h>
+#include <stdio.h>
+
+static void fwrite_callback(const char *data, size_t len, void *opaque) {
+    fwrite(data, len, 1, (FILE *)opaque);
+}
+
+int main() {
+    const char *sym = "_RNvNtCsbmNqQUJIY6D_4core3foo3bar";
+
+    printf("demangle(%s) = ", sym);
+    rust_demangle_with_callback(sym, 0, fwrite_callback, stdout);
+    printf("\n");
+
+    printf("demangle(%s, VERBOSE) = ", sym);
+    rust_demangle_with_callback(
+        sym, RUST_DEMANGLE_FLAG_VERBOSE, fwrite_callback, stdout
+    );
+    printf("\n");
+}
+```
+(with identical output to the simpler example)
+
+## Testing
+
+`cargo test` will run built-in tests - it's implemented in Rust (in `test-harness`)
+so that it can depend on `rustc-demangle` itself for comparisons.
+
+Additionally, `cargo run -q --release --example check-csv-dataset path/to/syms/*.csv`
+can be used to provide CSV files with additional mangled symbols test data, but such
+datasets aren't trivial to obtain (existing ones required building `rust-lang/rust`
+with a compiler patch that reacts to a custom environment variable).
+They're also quite large (~1GiB uncompressed) so none have been published anywhere yet.
+
+## History
+
+This C port was started while the [Rust RFC2603 (aka "`v0`") mangling scheme](https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html)
+was still being developed, with the intent of upstreaming it into `libiberty`
+(which provides demangling for `binutils`, `gdb`, Linux `perf`, etc.) and other
+projects (e.g. `valgrind`) - you can see some of that upstreaming history
+[on the `v0` tracking issue](https://github.com/rust-lang/rust/issues/60705).
+
+At the time, the expectation was that most projects could either depend on
+`libiberty`, or vendor a copy of its code, so the C port focused on upstreaming
+to it, rather than producing an independent reusable C codebase.
+
+That meant that instead of a `git` repository, the [code revisions were only tracked by a gist](https://gist.github.com/eddyb/c41a69378750a433767cf53fe2316768/revisions),
+and the GNU code style was followed (including C89 comments and variable declarations).
+
+However, the LGPL license of `libiberty` turned out to be a problem for adoption,
+compared to the typical MIT/Apache-2.0 dual licensing of Rust projects.
+
+### The `rust-demangle.c` fork
+
+This repository started out as a fork of [the original gist](https://gist.github.com/eddyb/c41a69378750a433767cf53fe2316768/revisions), at commit [`e2c30407516a87c0f8c3820cf152640bd08805dd`](https://github.com/LykenSol/rust-demangle.c/commit/e2c30407516a87c0f8c3820cf152640bd08805dd), *just before `libiberty`
+integration* (which was in commit [`0e6f57b0e86ccec4395f8850f4885b1e391a9f4b`](https://gist.github.com/eddyb/c41a69378750a433767cf53fe2316768/0e6f57b0e86ccec4395f8850f4885b1e391a9f4b)).
+
+Any changes since that gist are either fresh C ports of the Rust `rustc-demangle`
+code, or completely new code, in order to maintain the [MIT/Apache-2.0 dual licensing](#license).
+
+While this has the disadvantage of starting behind `libiberty` (which kept its
+Rust `legacy` demangler, and also got a few more features during/since upstreaming),
+the relationship may reverse eventually, where this port could get new features
+that would then have to be upstreamed into `libiberty`.
+
+## License
+
+[Like `rustc-demangle`](https://github.com/rust-lang/rustc-demangle#license), this project is licensed under either of
+
+ * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
+   http://www.apache.org/licenses/LICENSE-2.0)
+ * MIT license ([LICENSE-MIT](LICENSE-MIT) or
+   http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in `rust-demangle.c` you, as defined in the Apache-2.0 license, shall
+be dual licensed as above, without any additional terms or conditions.
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/rust-demangle.c 1.4.0+dfsg-1/third-party/rust-demangle/rust-demangle.c
--- 1.3.1+dfsg-1/third-party/rust-demangle/rust-demangle.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/rust-demangle.c	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,937 @@
+// FIXME(eddyb) should this use `<rust-demangle.h>`?
+#include "rust-demangle.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct rust_demangler {
+    const char *sym;
+    size_t sym_len;
+
+    void *callback_opaque;
+    void (*callback)(const char *data, size_t len, void *opaque);
+
+    // Position of the next character to read from the symbol.
+    size_t next;
+
+    // `true` if any error occurred.
+    bool errored;
+
+    // `true` if nothing should be printed.
+    bool skipping_printing;
+
+    // `true` if printing should be verbose (e.g. include hashes).
+    bool verbose;
+
+    // Rust mangling version, with legacy mangling being -1.
+    int version;
+
+    uint64_t bound_lifetime_depth;
+};
+
+#define ERROR_AND(x)                                                           \
+    do {                                                                       \
+        rdm->errored = true;                                                   \
+        x;                                                                     \
+    } while (0)
+#define CHECK_OR(cond, x)                                                      \
+    do {                                                                       \
+        if (!(cond))                                                           \
+            ERROR_AND(x);                                                      \
+    } while (0)
+
+// FIXME(eddyb) consider renaming these to not start with `IS` (UB?).
+#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
+#define IS_UPPER(c) ((c) >= 'A' && (c) <= 'Z')
+#define IS_LOWER(c) ((c) >= 'a' && (c) <= 'z')
+
+// Parsing functions.
+
+static char peek(const struct rust_demangler *rdm) {
+    if (rdm->next < rdm->sym_len)
+        return rdm->sym[rdm->next];
+    return 0;
+}
+
+static bool eat(struct rust_demangler *rdm, char c) {
+    if (peek(rdm) == c) {
+        rdm->next++;
+        return true;
+    } else
+        return false;
+}
+
+static char next(struct rust_demangler *rdm) {
+    char c = peek(rdm);
+    CHECK_OR(c, return 0);
+    rdm->next++;
+    return c;
+}
+
+static uint64_t parse_integer_62(struct rust_demangler *rdm) {
+    if (eat(rdm, '_'))
+        return 0;
+
+    uint64_t x = 0;
+    while (!eat(rdm, '_')) {
+        char c = next(rdm);
+        x *= 62;
+        if (IS_DIGIT(c))
+            x += c - '0';
+        else if (IS_LOWER(c))
+            x += 10 + (c - 'a');
+        else if (IS_UPPER(c))
+            x += 10 + 26 + (c - 'A');
+        else
+            ERROR_AND(return 0);
+    }
+    return x + 1;
+}
+
+static uint64_t parse_opt_integer_62(struct rust_demangler *rdm, char tag) {
+    if (!eat(rdm, tag))
+        return 0;
+    return 1 + parse_integer_62(rdm);
+}
+
+static uint64_t parse_disambiguator(struct rust_demangler *rdm) {
+    return parse_opt_integer_62(rdm, 's');
+}
+
+struct rust_mangled_ident {
+    // ASCII part of the identifier.
+    const char *ascii;
+    size_t ascii_len;
+
+    // Punycode insertion codes for Unicode codepoints, if any.
+    const char *punycode;
+    size_t punycode_len;
+};
+
+static struct rust_mangled_ident parse_ident(struct rust_demangler *rdm) {
+    struct rust_mangled_ident ident;
+
+    ident.ascii = NULL;
+    ident.ascii_len = 0;
+    ident.punycode = NULL;
+    ident.punycode_len = 0;
+
+    bool is_punycode = eat(rdm, 'u');
+
+    char c = next(rdm);
+    CHECK_OR(IS_DIGIT(c), return ident);
+    size_t len = c - '0';
+
+    if (c != '0')
+        while (IS_DIGIT(peek(rdm)))
+            len = len * 10 + (next(rdm) - '0');
+
+    // Skip past the optional `_` separator.
+    eat(rdm, '_');
+
+    size_t start = rdm->next;
+    rdm->next += len;
+    // Check for overflows.
+    CHECK_OR((start <= rdm->next) && (rdm->next <= rdm->sym_len), return ident);
+
+    ident.ascii = rdm->sym + start;
+    ident.ascii_len = len;
+
+    if (is_punycode) {
+        ident.punycode_len = 0;
+        while (ident.ascii_len > 0) {
+            ident.ascii_len--;
+
+            // The last '_' is a separator between ascii & punycode.
+            if (ident.ascii[ident.ascii_len] == '_')
+                break;
+
+            ident.punycode_len++;
+        }
+        CHECK_OR(ident.punycode_len > 0, return ident);
+        ident.punycode = ident.ascii + (len - ident.punycode_len);
+    }
+
+    if (ident.ascii_len == 0)
+        ident.ascii = NULL;
+
+    return ident;
+}
+
+// Printing functions.
+
+static void
+print_str(struct rust_demangler *rdm, const char *data, size_t len) {
+    if (!rdm->errored && !rdm->skipping_printing)
+        rdm->callback(data, len, rdm->callback_opaque);
+}
+
+#define PRINT(s) print_str(rdm, s, strlen(s))
+
+static void print_uint64(struct rust_demangler *rdm, uint64_t x) {
+    char s[21];
+    sprintf(s, "%" PRIu64, x);
+    PRINT(s);
+}
+
+static void print_uint64_hex(struct rust_demangler *rdm, uint64_t x) {
+    char s[17];
+    sprintf(s, "%" PRIx64, x);
+    PRINT(s);
+}
+
+static void
+print_ident(struct rust_demangler *rdm, struct rust_mangled_ident ident) {
+    if (rdm->errored || rdm->skipping_printing)
+        return;
+
+    if (!ident.punycode) {
+        print_str(rdm, ident.ascii, ident.ascii_len);
+        return;
+    }
+
+    size_t len = 0;
+    size_t cap = 4;
+    while (cap < ident.ascii_len) {
+        cap *= 2;
+        // Check for overflows.
+        CHECK_OR((cap * 4) / 4 == cap, return );
+    }
+
+    // Store the output codepoints as groups of 4 UTF-8 bytes.
+    uint8_t *out = (uint8_t *)malloc(cap * 4);
+    CHECK_OR(out, return );
+
+    // Populate initial output from ASCII fragment.
+    for (len = 0; len < ident.ascii_len; len++) {
+        uint8_t *p = out + 4 * len;
+        p[0] = 0;
+        p[1] = 0;
+        p[2] = 0;
+        p[3] = ident.ascii[len];
+    }
+
+    // Punycode parameters and initial state.
+    size_t base = 36;
+    size_t t_min = 1;
+    size_t t_max = 26;
+    size_t skew = 38;
+    size_t damp = 700;
+    size_t bias = 72;
+    size_t i = 0;
+    uint32_t c = 0x80;
+
+    size_t punycode_pos = 0;
+    while (punycode_pos < ident.punycode_len) {
+        // Read one delta value.
+        size_t delta = 0;
+        size_t w = 1;
+        size_t k = 0;
+        size_t t;
+        uint8_t d;
+        do {
+            k += base;
+            t = k < bias ? 0 : (k - bias);
+            if (t < t_min)
+                t = t_min;
+            if (t > t_max)
+                t = t_max;
+
+            CHECK_OR(punycode_pos < ident.punycode_len, goto cleanup);
+            d = ident.punycode[punycode_pos++];
+
+            if (IS_LOWER(d))
+                d = d - 'a';
+            else if (IS_DIGIT(d))
+                d = 26 + (d - '0');
+            else
+                ERROR_AND(goto cleanup);
+
+            delta += d * w;
+            w *= base - t;
+        } while (d >= t);
+
+        // Compute the new insert position and character.
+        len++;
+        i += delta;
+        c += i / len;
+        i %= len;
+
+        // Ensure enough space is available.
+        if (cap < len) {
+            cap *= 2;
+            // Check for overflows.
+            CHECK_OR((cap * 4) / 4 == cap, goto cleanup);
+            CHECK_OR(cap >= len, goto cleanup);
+        }
+        uint8_t *p = (uint8_t *)realloc(out, cap * 4);
+        CHECK_OR(p, goto cleanup);
+        out = p;
+
+        // Move the characters after the insert position.
+        p = out + i * 4;
+        memmove(p + 4, p, (len - i - 1) * 4);
+
+        // Insert the new character, as UTF-8 bytes.
+        p[0] = c >= 0x10000 ? 0xf0 | (c >> 18) : 0;
+        p[1] =
+            c >= 0x800 ? (c < 0x10000 ? 0xe0 : 0x80) | ((c >> 12) & 0x3f) : 0;
+        p[2] = (c < 0x800 ? 0xc0 : 0x80) | ((c >> 6) & 0x3f);
+        p[3] = 0x80 | (c & 0x3f);
+
+        // If there are no more deltas, decoding is complete.
+        if (punycode_pos == ident.punycode_len)
+            break;
+
+        i++;
+
+        // Perform bias adaptation.
+        delta /= damp;
+        damp = 2;
+
+        delta += delta / len;
+        k = 0;
+        while (delta > ((base - t_min) * t_max) / 2) {
+            delta /= base - t_min;
+            k += base;
+        }
+        bias = k + ((base - t_min + 1) * delta) / (delta + skew);
+    }
+
+    // Remove all the 0 bytes to leave behind an UTF-8 string.
+    size_t j;
+    for (i = 0, j = 0; i < len * 4; i++)
+        if (out[i] != 0)
+            out[j++] = out[i];
+
+    print_str(rdm, (const char *)out, j);
+
+cleanup:
+    free(out);
+}
+
+/// Print the lifetime according to the previously decoded index.
+/// An index of `0` always refers to `'_`, but starting with `1`,
+/// indices refer to late-bound lifetimes introduced by a binder.
+static void print_lifetime_from_index(struct rust_demangler *rdm, uint64_t lt) {
+    PRINT("'");
+    if (lt == 0) {
+        PRINT("_");
+        return;
+    }
+
+    uint64_t depth = rdm->bound_lifetime_depth - lt;
+    // Try to print lifetimes alphabetically first.
+    if (depth < 26) {
+        char c = 'a' + depth;
+        print_str(rdm, &c, 1);
+    } else {
+        // Use `'_123` after running out of letters.
+        PRINT("_");
+        print_uint64(rdm, depth);
+    }
+}
+
+// Demangling functions.
+
+static void demangle_binder(struct rust_demangler *rdm);
+static void demangle_path(struct rust_demangler *rdm, bool in_value);
+static void demangle_generic_arg(struct rust_demangler *rdm);
+static void demangle_type(struct rust_demangler *rdm);
+static bool demangle_path_maybe_open_generics(struct rust_demangler *rdm);
+static void demangle_dyn_trait(struct rust_demangler *rdm);
+static void demangle_const(struct rust_demangler *rdm);
+static void demangle_const_uint(struct rust_demangler *rdm);
+
+/// Optionally enter a binder ('G') for late-bound lifetimes,
+/// printing e.g. `for<'a, 'b> `, and make those lifetimes visible
+/// to the caller (via depth level, which the caller should reset).
+static void demangle_binder(struct rust_demangler *rdm) {
+    CHECK_OR(!rdm->errored, return );
+
+    uint64_t bound_lifetimes = parse_opt_integer_62(rdm, 'G');
+    if (bound_lifetimes > 0) {
+        PRINT("for<");
+        for (uint64_t i = 0; i < bound_lifetimes; i++) {
+            if (i > 0)
+                PRINT(", ");
+            rdm->bound_lifetime_depth++;
+            print_lifetime_from_index(rdm, 1);
+        }
+        PRINT("> ");
+    }
+}
+
+static void demangle_path(struct rust_demangler *rdm, bool in_value) {
+    CHECK_OR(!rdm->errored, return );
+
+    char tag = next(rdm);
+    switch (tag) {
+    case 'C': {
+        uint64_t dis = parse_disambiguator(rdm);
+        struct rust_mangled_ident name = parse_ident(rdm);
+
+        print_ident(rdm, name);
+        if (rdm->verbose) {
+            PRINT("[");
+            print_uint64_hex(rdm, dis);
+            PRINT("]");
+        }
+        break;
+    }
+    case 'N': {
+        char ns = next(rdm);
+        CHECK_OR(IS_LOWER(ns) || IS_UPPER(ns), return );
+
+        demangle_path(rdm, in_value);
+
+        uint64_t dis = parse_disambiguator(rdm);
+        struct rust_mangled_ident name = parse_ident(rdm);
+
+        if (IS_UPPER(ns)) {
+            // Special namespaces, like closures and shims.
+            PRINT("::{");
+            switch (ns) {
+            case 'C':
+                PRINT("closure");
+                break;
+            case 'S':
+                PRINT("shim");
+                break;
+            default:
+                print_str(rdm, &ns, 1);
+            }
+            if (name.ascii || name.punycode) {
+                PRINT(":");
+                print_ident(rdm, name);
+            }
+            PRINT("#");
+            print_uint64(rdm, dis);
+            PRINT("}");
+        } else {
+            // Implementation-specific/unspecified namespaces.
+
+            if (name.ascii || name.punycode) {
+                PRINT("::");
+                print_ident(rdm, name);
+            }
+        }
+        break;
+    }
+    case 'M':
+    case 'X':
+        // Ignore the `impl`'s own path.
+        parse_disambiguator(rdm);
+        bool was_skipping_printing = rdm->skipping_printing;
+        rdm->skipping_printing = true;
+        demangle_path(rdm, in_value);
+        rdm->skipping_printing = was_skipping_printing;
+    case 'Y':
+        PRINT("<");
+        demangle_type(rdm);
+        if (tag != 'M') {
+            PRINT(" as ");
+            demangle_path(rdm, false);
+        }
+        PRINT(">");
+        break;
+    case 'I':
+        demangle_path(rdm, in_value);
+        if (in_value)
+            PRINT("::");
+        PRINT("<");
+        for (size_t i = 0; !rdm->errored && !eat(rdm, 'E'); i++) {
+            if (i > 0)
+                PRINT(", ");
+            demangle_generic_arg(rdm);
+        }
+        PRINT(">");
+        break;
+    case 'B': {
+        size_t backref = parse_integer_62(rdm);
+        if (!rdm->skipping_printing) {
+            size_t old_next = rdm->next;
+            rdm->next = backref;
+            demangle_path(rdm, in_value);
+            rdm->next = old_next;
+        }
+        break;
+    }
+    default:
+        ERROR_AND(return );
+    }
+}
+
+static void demangle_generic_arg(struct rust_demangler *rdm) {
+    if (eat(rdm, 'L')) {
+        uint64_t lt = parse_integer_62(rdm);
+        print_lifetime_from_index(rdm, lt);
+    } else if (eat(rdm, 'K'))
+        demangle_const(rdm);
+    else
+        demangle_type(rdm);
+}
+
+static const char *basic_type(char tag) {
+    switch (tag) {
+    case 'b':
+        return "bool";
+    case 'c':
+        return "char";
+    case 'e':
+        return "str";
+    case 'u':
+        return "()";
+    case 'a':
+        return "i8";
+    case 's':
+        return "i16";
+    case 'l':
+        return "i32";
+    case 'x':
+        return "i64";
+    case 'n':
+        return "i128";
+    case 'i':
+        return "isize";
+    case 'h':
+        return "u8";
+    case 't':
+        return "u16";
+    case 'm':
+        return "u32";
+    case 'y':
+        return "u64";
+    case 'o':
+        return "u128";
+    case 'j':
+        return "usize";
+    case 'f':
+        return "f32";
+    case 'd':
+        return "f64";
+    case 'z':
+        return "!";
+    case 'p':
+        return "_";
+    case 'v':
+        return "...";
+
+    default:
+        return NULL;
+    }
+}
+
+static void demangle_type(struct rust_demangler *rdm) {
+    CHECK_OR(!rdm->errored, return );
+
+    char tag = next(rdm);
+
+    const char *basic = basic_type(tag);
+    if (basic) {
+        PRINT(basic);
+        return;
+    }
+
+    switch (tag) {
+    case 'R':
+    case 'Q':
+        PRINT("&");
+        if (eat(rdm, 'L')) {
+            uint64_t lt = parse_integer_62(rdm);
+            if (lt) {
+                print_lifetime_from_index(rdm, lt);
+                PRINT(" ");
+            }
+        }
+        if (tag != 'R')
+            PRINT("mut ");
+        demangle_type(rdm);
+        break;
+    case 'P':
+    case 'O':
+        PRINT("*");
+        if (tag != 'P')
+            PRINT("mut ");
+        else
+            PRINT("const ");
+        demangle_type(rdm);
+        break;
+    case 'A':
+    case 'S':
+        PRINT("[");
+        demangle_type(rdm);
+        if (tag == 'A') {
+            PRINT("; ");
+            demangle_const(rdm);
+        }
+        PRINT("]");
+        break;
+    case 'T':
+        PRINT("(");
+        size_t i;
+        for (i = 0; !rdm->errored && !eat(rdm, 'E'); i++) {
+            if (i > 0)
+                PRINT(", ");
+            demangle_type(rdm);
+        }
+        if (i == 1)
+            PRINT(",");
+        PRINT(")");
+        break;
+    case 'F': {
+        uint64_t old_bound_lifetime_depth = rdm->bound_lifetime_depth;
+        demangle_binder(rdm);
+
+        if (eat(rdm, 'U'))
+            PRINT("unsafe ");
+
+        if (eat(rdm, 'K')) {
+            struct rust_mangled_ident abi;
+
+            if (eat(rdm, 'C')) {
+                abi.ascii = "C";
+                abi.ascii_len = 1;
+            } else {
+                abi = parse_ident(rdm);
+                CHECK_OR(abi.ascii && !abi.punycode, goto restore);
+            }
+
+            PRINT("extern \"");
+
+            // If the ABI had any `-`, they were replaced with `_`,
+            // so the parts between `_` have to be re-joined with `-`.
+            for (size_t i = 0; i < abi.ascii_len; i++) {
+                if (abi.ascii[i] == '_') {
+                    print_str(rdm, abi.ascii, i);
+                    PRINT("-");
+                    abi.ascii += i + 1;
+                    abi.ascii_len -= i + 1;
+                    i = 0;
+                }
+            }
+            print_str(rdm, abi.ascii, abi.ascii_len);
+
+            PRINT("\" ");
+        }
+
+        PRINT("fn(");
+        for (size_t i = 0; !rdm->errored && !eat(rdm, 'E'); i++) {
+            if (i > 0)
+                PRINT(", ");
+            demangle_type(rdm);
+        }
+        PRINT(")");
+
+        if (eat(rdm, 'u')) {
+            // Skip printing the return type if it's 'u', i.e. `()`.
+        } else {
+            PRINT(" -> ");
+            demangle_type(rdm);
+        }
+
+    // Restore `bound_lifetime_depth` to outside the binder.
+    restore:
+        rdm->bound_lifetime_depth = old_bound_lifetime_depth;
+        break;
+    }
+    case 'D':
+        PRINT("dyn ");
+
+        uint64_t old_bound_lifetime_depth = rdm->bound_lifetime_depth;
+        demangle_binder(rdm);
+
+        for (size_t i = 0; !rdm->errored && !eat(rdm, 'E'); i++) {
+            if (i > 0)
+                PRINT(" + ");
+            demangle_dyn_trait(rdm);
+        }
+
+        // Restore `bound_lifetime_depth` to outside the binder.
+        rdm->bound_lifetime_depth = old_bound_lifetime_depth;
+
+        CHECK_OR(eat(rdm, 'L'), return );
+        uint64_t lt = parse_integer_62(rdm);
+        if (lt) {
+            PRINT(" + ");
+            print_lifetime_from_index(rdm, lt);
+        }
+        break;
+    case 'B': {
+        size_t backref = parse_integer_62(rdm);
+        if (!rdm->skipping_printing) {
+            size_t old_next = rdm->next;
+            rdm->next = backref;
+            demangle_type(rdm);
+            rdm->next = old_next;
+        }
+        break;
+    }
+    default:
+        // Go back to the tag, so `demangle_path` also sees it.
+        rdm->next--;
+        demangle_path(rdm, false);
+    }
+}
+
+/// A trait in a trait object may have some "existential projections"
+/// (i.e. associated type bindings) after it, which should be printed
+/// in the `<...>` of the trait, e.g. `dyn Trait<T, U, Assoc=X>`.
+/// To this end, this method will keep the `<...>` of an 'I' path
+/// open, by omitting the `>`, and return `Ok(true)` in that case.
+static bool demangle_path_maybe_open_generics(struct rust_demangler *rdm) {
+    bool open = false;
+
+    CHECK_OR(!rdm->errored, return open);
+
+    if (eat(rdm, 'B')) {
+        size_t backref = parse_integer_62(rdm);
+        if (!rdm->skipping_printing) {
+            size_t old_next = rdm->next;
+            rdm->next = backref;
+            open = demangle_path_maybe_open_generics(rdm);
+            rdm->next = old_next;
+        }
+    } else if (eat(rdm, 'I')) {
+        demangle_path(rdm, false);
+        PRINT("<");
+        open = true;
+        for (size_t i = 0; !rdm->errored && !eat(rdm, 'E'); i++) {
+            if (i > 0)
+                PRINT(", ");
+            demangle_generic_arg(rdm);
+        }
+    } else
+        demangle_path(rdm, false);
+    return open;
+}
+
+static void demangle_dyn_trait(struct rust_demangler *rdm) {
+    CHECK_OR(!rdm->errored, return );
+
+    bool open = demangle_path_maybe_open_generics(rdm);
+
+    while (eat(rdm, 'p')) {
+        if (!open)
+            PRINT("<");
+        else
+            PRINT(", ");
+        open = true;
+
+        struct rust_mangled_ident name = parse_ident(rdm);
+        print_ident(rdm, name);
+        PRINT(" = ");
+        demangle_type(rdm);
+    }
+
+    if (open)
+        PRINT(">");
+}
+
+static void demangle_const(struct rust_demangler *rdm) {
+    CHECK_OR(!rdm->errored, return );
+
+    if (eat(rdm, 'B')) {
+        size_t backref = parse_integer_62(rdm);
+        if (!rdm->skipping_printing) {
+            size_t old_next = rdm->next;
+            rdm->next = backref;
+            demangle_const(rdm);
+            rdm->next = old_next;
+        }
+        return;
+    }
+
+    char ty_tag = next(rdm);
+    switch (ty_tag) {
+    // Unsigned integer types.
+    case 'h':
+    case 't':
+    case 'm':
+    case 'y':
+    case 'o':
+    case 'j':
+        break;
+
+    default:
+        ERROR_AND(return );
+    }
+
+    if (eat(rdm, 'p'))
+        PRINT("_");
+    else {
+        demangle_const_uint(rdm);
+        if (rdm->verbose)
+            PRINT(basic_type(ty_tag));
+    }
+}
+
+static void demangle_const_uint(struct rust_demangler *rdm) {
+    CHECK_OR(!rdm->errored, return );
+
+    uint64_t value = 0;
+    size_t hex_len = 0;
+    while (!eat(rdm, '_')) {
+        value <<= 4;
+
+        char c = next(rdm);
+        if (IS_DIGIT(c))
+            value |= c - '0';
+        else if (c >= 'a' && c <= 'f')
+            value |= 10 + (c - 'a');
+        else
+            ERROR_AND(return );
+        hex_len++;
+    }
+
+    // Print anything that doesn't fit in `uint64_t` verbatim.
+    if (hex_len > 16) {
+        PRINT("0x");
+        print_str(rdm, rdm->sym + (rdm->next - hex_len), hex_len);
+        return;
+    }
+
+    print_uint64(rdm, value);
+}
+
+bool rust_demangle_with_callback(
+    const char *mangled, int flags,
+    void (*callback)(const char *data, size_t len, void *opaque), void *opaque
+) {
+    // Rust symbols always start with _R.
+    if (mangled[0] == '_' && mangled[1] == 'R')
+        mangled += 2;
+    else
+        return false;
+
+    // Paths always start with uppercase characters.
+    if (!IS_UPPER(mangled[0]))
+        return false;
+
+    struct rust_demangler rdm;
+
+    rdm.sym = mangled;
+    rdm.sym_len = 0;
+
+    rdm.callback_opaque = opaque;
+    rdm.callback = callback;
+
+    rdm.next = 0;
+    rdm.errored = false;
+    rdm.skipping_printing = false;
+    rdm.verbose = (flags & RUST_DEMANGLE_FLAG_VERBOSE) != 0;
+    rdm.version = 0;
+    rdm.bound_lifetime_depth = 0;
+
+    // Rust symbols use only [_0-9a-zA-Z] characters.
+    for (const char *p = mangled; *p; p++) {
+        if (!(*p == '_' || IS_DIGIT(*p) || IS_LOWER(*p) || IS_UPPER(*p)))
+            return false;
+        rdm.sym_len++;
+    }
+
+    demangle_path(&rdm, true);
+
+    // Skip instantiating crate.
+    if (!rdm.errored && rdm.next < rdm.sym_len) {
+        rdm.skipping_printing = true;
+        demangle_path(&rdm, false);
+    }
+
+    // It's an error to not reach the end.
+    rdm.errored |= rdm.next != rdm.sym_len;
+
+    return !rdm.errored;
+}
+
+// Growable string buffers.
+struct str_buf {
+    char *ptr;
+    size_t len;
+    size_t cap;
+    bool errored;
+};
+
+static void str_buf_reserve(struct str_buf *buf, size_t extra) {
+    // Allocation failed before.
+    if (buf->errored)
+        return;
+
+    size_t available = buf->cap - buf->len;
+
+    if (extra <= available)
+        return;
+
+    size_t min_new_cap = buf->cap + (extra - available);
+
+    // Check for overflows.
+    if (min_new_cap < buf->cap) {
+        buf->errored = true;
+        return;
+    }
+
+    size_t new_cap = buf->cap;
+
+    if (new_cap == 0)
+        new_cap = 4;
+
+    // Double capacity until sufficiently large.
+    while (new_cap < min_new_cap) {
+        new_cap *= 2;
+
+        // Check for overflows.
+        if (new_cap < buf->cap) {
+            buf->errored = true;
+            return;
+        }
+    }
+
+    char *new_ptr = (char *)realloc(buf->ptr, new_cap);
+    if (new_ptr == NULL) {
+        free(buf->ptr);
+        buf->ptr = NULL;
+        buf->len = 0;
+        buf->cap = 0;
+        buf->errored = true;
+    } else {
+        buf->ptr = new_ptr;
+        buf->cap = new_cap;
+    }
+}
+
+static void str_buf_append(struct str_buf *buf, const char *data, size_t len) {
+    str_buf_reserve(buf, len);
+    if (buf->errored)
+        return;
+
+    memcpy(buf->ptr + buf->len, data, len);
+    buf->len += len;
+}
+
+static void
+str_buf_demangle_callback(const char *data, size_t len, void *opaque) {
+    str_buf_append(opaque, data, len);
+}
+
+char *rust_demangle(const char *mangled, int flags) {
+    struct str_buf out;
+
+    out.ptr = NULL;
+    out.len = 0;
+    out.cap = 0;
+    out.errored = false;
+
+    bool success = rust_demangle_with_callback(
+        mangled, flags, str_buf_demangle_callback, &out
+    );
+
+    if (!success) {
+        free(out.ptr);
+        return NULL;
+    }
+
+    str_buf_append(&out, "\0", 1);
+    return out.ptr;
+}
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/rust-demangle.h 1.4.0+dfsg-1/third-party/rust-demangle/rust-demangle.h
--- 1.3.1+dfsg-1/third-party/rust-demangle/rust-demangle.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/rust-demangle.h	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,18 @@
+#include <stdbool.h>
+#include <stddef.h>
+
+#define RUST_DEMANGLE_FLAG_VERBOSE 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool rust_demangle_with_callback(
+    const char *mangled, int flags,
+    void (*callback)(const char *data, size_t len, void *opaque), void *opaque
+);
+char *rust_demangle(const char *mangled, int flags);
+
+#ifdef __cplusplus
+}
+#endif
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/test-harness/build.rs 1.4.0+dfsg-1/third-party/rust-demangle/test-harness/build.rs
--- 1.3.1+dfsg-1/third-party/rust-demangle/test-harness/build.rs	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/test-harness/build.rs	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,15 @@
+fn main() {
+    let src = "../rust-demangle.c";
+    let header = "../rust-demangle.h";
+    println!("cargo:rerun-if-changed={}", src);
+    println!("cargo:rerun-if-changed={}", header);
+
+    cc::Build::new()
+        .file("../rust-demangle.c")
+        .flag_if_supported("-std=c99")
+        .flag_if_supported("-pedantic")
+        .warnings(true)
+        .warnings_into_errors(true)
+        .flag_if_supported("-Werror=uninitialized")
+        .compile("rust-demangle");
+}
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/test-harness/Cargo.toml 1.4.0+dfsg-1/third-party/rust-demangle/test-harness/Cargo.toml
--- 1.3.1+dfsg-1/third-party/rust-demangle/test-harness/Cargo.toml	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/test-harness/Cargo.toml	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,17 @@
+[package]
+name = "rust-demangle-c-test-harness"
+version = "0.0.0"
+license = "MIT OR Apache-2.0"
+edition = "2021"
+publish = false
+
+# Tests go into `tests/`, not in `src/`.
+[lib]
+test = false
+doctest = false
+
+[dependencies]
+rustc-demangle = "0.1.21"
+
+[build-dependencies]
+cc = "1.0"
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/test-harness/examples/check-csv-dataset.rs 1.4.0+dfsg-1/third-party/rust-demangle/test-harness/examples/check-csv-dataset.rs
--- 1.3.1+dfsg-1/third-party/rust-demangle/test-harness/examples/check-csv-dataset.rs	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/test-harness/examples/check-csv-dataset.rs	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,29 @@
+use std::env;
+use std::fs::File;
+use std::io::prelude::*;
+use std::io::BufReader;
+use std::path::PathBuf;
+
+use rust_demangle_c_test_harness::demangle;
+
+// HACK(eddyb) this is only an `example` so that `cargo run` doesn't do anything.
+// FIXME(eddyb) document this better and provide datasets for it.
+fn main() {
+    let header = "legacy+generics,legacy,mw,mw+compression,v0,v0+compression";
+
+    for path in env::args_os().skip(1).map(PathBuf::from) {
+        let mut lines = BufReader::new(File::open(path).unwrap())
+            .lines()
+            .map(|l| l.unwrap());
+
+        assert_eq!(lines.next().unwrap(), header);
+
+        for line in lines {
+            for mangling in line.split(',').skip(4) {
+                for verbose in [false, true] {
+                    demangle(mangling).to_string_maybe_verbose(verbose);
+                }
+            }
+        }
+    }
+}
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/test-harness/src/lib.rs 1.4.0+dfsg-1/third-party/rust-demangle/test-harness/src/lib.rs
--- 1.3.1+dfsg-1/third-party/rust-demangle/test-harness/src/lib.rs	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/test-harness/src/lib.rs	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,115 @@
+use std::fmt;
+
+// HACK(eddyb) helper macros for tests.
+#[macro_export]
+macro_rules! assert_contains {
+    ($s:expr, $needle:expr) => {{
+        let (s, needle) = ($s, $needle);
+        assert!(
+            s.contains(needle),
+            "{:?} should've contained {:?}",
+            s,
+            needle
+        );
+    }};
+}
+#[macro_export]
+macro_rules! assert_ends_with {
+    ($s:expr, $suffix:expr) => {{
+        let (s, suffix) = ($s, $suffix);
+        assert!(
+            s.ends_with(suffix),
+            "{:?} should've ended in {:?}",
+            s,
+            suffix
+        );
+    }};
+}
+
+/// `rustc_demangle::Demangle` wrapper that will also attempt demanging with
+/// `rust-demangle.c`'s `rust_demangle` when formatted, and assert equality.
+///
+/// The reason this mimics `rustc_demangle`'s API is to allow its tests to be
+/// reused without rewriting them (which could risk introducing bugs).
+pub struct Demangle<'a> {
+    // NOTE(eddyb) we don't trust `rustc_demangle` to keep the original string
+    // unmodified, as if it e.g. strips suffixes early, it could hide the fact
+    // that the C port doesn't have that sort of thing supported yet.
+    original: &'a str,
+
+    rustc_demangle: rustc_demangle::Demangle<'a>,
+}
+
+pub fn demangle(s: &str) -> Demangle {
+    Demangle {
+        original: s,
+        rustc_demangle: rustc_demangle::demangle(s),
+    }
+}
+
+pub fn try_demangle(s: &str) -> Result<Demangle<'_>, rustc_demangle::TryDemangleError> {
+    match rustc_demangle::try_demangle(s) {
+        Ok(d) => Ok(Demangle {
+            original: s,
+            rustc_demangle: d,
+        }),
+        Err(e) => {
+            // HACK(eddyb) ensure the C port also errors.
+            assert_eq!(demangle_via_c(s, false), "");
+
+            Err(e)
+        }
+    }
+}
+
+impl fmt::Display for Demangle<'_> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str(&self.to_string_maybe_verbose(!f.alternate()))
+    }
+}
+
+impl Demangle<'_> {
+    pub fn to_string_maybe_verbose(&self, verbose: bool) -> String {
+        let rust = if verbose {
+            format!("{}", self.rustc_demangle)
+        } else {
+            format!("{:#}", self.rustc_demangle)
+        };
+        let c = demangle_via_c(self.original, verbose);
+        if rust != c {
+            panic!(
+                "Rust vs C demangling difference:\
+            \n mangled: {mangled:?}\
+            \n    rust: {rust:?}\
+            \n       c: {c:?}\
+            \n",
+                mangled = self.original
+            );
+        }
+
+        rust
+    }
+}
+
+fn demangle_via_c(mangled: &str, verbose: bool) -> String {
+    use std::ffi::{CStr, CString};
+    use std::os::raw::c_char;
+
+    extern "C" {
+        fn rust_demangle(mangled: *const c_char, flags: i32) -> *mut c_char;
+        fn free(ptr: *mut c_char);
+    }
+
+    let flags = if verbose { 1 } else { 0 };
+    let mangled = CString::new(mangled).unwrap();
+    let out = unsafe { rust_demangle(mangled.as_ptr(), flags) };
+    if out.is_null() {
+        String::new()
+    } else {
+        unsafe {
+            let s = CStr::from_ptr(out).to_string_lossy().into_owned();
+            free(out);
+            s
+        }
+    }
+}
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/test-harness/tests/legacy.rs 1.4.0+dfsg-1/third-party/rust-demangle/test-harness/tests/legacy.rs
--- 1.3.1+dfsg-1/third-party/rust-demangle/test-harness/tests/legacy.rs	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/test-harness/tests/legacy.rs	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,241 @@
+//! Tests copied from `https://github.com/rust-lang/rustc-demangle`'s
+//! `src/legacy.rs` at `fd906f850f90f6d4845c7b8219d218293e0ab3ed`.
+//!
+//! These are the only changes made to the tests:
+//! * `::` absolute paths -> `rust_demangle_c_test_harness::`
+//! * `#[should_panic]` was added to tests that don't pass yet
+
+macro_rules! t {
+    ($a:expr, $b:expr) => {
+        assert!(ok($a, $b))
+    };
+}
+
+macro_rules! t_err {
+    ($a:expr) => {
+        assert!(ok_err($a))
+    };
+}
+
+macro_rules! t_nohash {
+    ($a:expr, $b:expr) => {{
+        assert_eq!(
+            format!("{:#}", rust_demangle_c_test_harness::demangle($a)),
+            $b
+        );
+    }};
+}
+
+fn ok(sym: &str, expected: &str) -> bool {
+    match rust_demangle_c_test_harness::try_demangle(sym) {
+        Ok(s) => {
+            if s.to_string() == expected {
+                true
+            } else {
+                println!("\n{}\n!=\n{}\n", s, expected);
+                false
+            }
+        }
+        Err(_) => {
+            println!("error demangling");
+            false
+        }
+    }
+}
+
+fn ok_err(sym: &str) -> bool {
+    match rust_demangle_c_test_harness::try_demangle(sym) {
+        Ok(_) => {
+            println!("succeeded in demangling");
+            false
+        }
+        Err(_) => rust_demangle_c_test_harness::demangle(sym).to_string() == sym,
+    }
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle() {
+    t_err!("test");
+    t!("_ZN4testE", "test");
+    t_err!("_ZN4test");
+    t!("_ZN4test1a2bcE", "test::a::bc");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_dollars() {
+    t!("_ZN4$RP$E", ")");
+    t!("_ZN8$RF$testE", "&test");
+    t!("_ZN8$BP$test4foobE", "*test::foob");
+    t!("_ZN9$u20$test4foobE", " test::foob");
+    t!("_ZN35Bar$LT$$u5b$u32$u3b$$u20$4$u5d$$GT$E", "Bar<[u32; 4]>");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_many_dollars() {
+    t!("_ZN13test$u20$test4foobE", "test test::foob");
+    t!("_ZN12test$BP$test4foobE", "test*test::foob");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_osx() {
+    t!(
+        "__ZN5alloc9allocator6Layout9for_value17h02a996811f781011E",
+        "alloc::allocator::Layout::for_value::h02a996811f781011"
+    );
+    t!("__ZN38_$LT$core..option..Option$LT$T$GT$$GT$6unwrap18_MSG_FILE_LINE_COL17haf7cb8d5824ee659E", "<core::option::Option<T>>::unwrap::_MSG_FILE_LINE_COL::haf7cb8d5824ee659");
+    t!("__ZN4core5slice89_$LT$impl$u20$core..iter..traits..IntoIterator$u20$for$u20$$RF$$u27$a$u20$$u5b$T$u5d$$GT$9into_iter17h450e234d27262170E", "core::slice::<impl core::iter::traits::IntoIterator for &'a [T]>::into_iter::h450e234d27262170");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_windows() {
+    t!("ZN4testE", "test");
+    t!("ZN13test$u20$test4foobE", "test test::foob");
+    t!("ZN12test$RF$test4foobE", "test&test::foob");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_elements_beginning_with_underscore() {
+    t!("_ZN13_$LT$test$GT$E", "<test>");
+    t!("_ZN28_$u7b$$u7b$closure$u7d$$u7d$E", "{{closure}}");
+    t!("_ZN15__STATIC_FMTSTRE", "__STATIC_FMTSTR");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_trait_impls() {
+    t!(
+        "_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE",
+        "<Test + 'static as foo::Bar<Test>>::bar"
+    );
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_without_hash() {
+    let s = "_ZN3foo17h05af221e174051e9E";
+    t!(s, "foo::h05af221e174051e9");
+    t_nohash!(s, "foo");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_without_hash_edgecases() {
+    // One element, no hash.
+    t_nohash!("_ZN3fooE", "foo");
+    // Two elements, no hash.
+    t_nohash!("_ZN3foo3barE", "foo::bar");
+    // Longer-than-normal hash.
+    t_nohash!("_ZN3foo20h05af221e174051e9abcE", "foo");
+    // Shorter-than-normal hash.
+    t_nohash!("_ZN3foo5h05afE", "foo");
+    // Valid hash, but not at the end.
+    t_nohash!("_ZN17h05af221e174051e93fooE", "h05af221e174051e9::foo");
+    // Not a valid hash, missing the 'h'.
+    t_nohash!("_ZN3foo16ffaf221e174051e9E", "foo::ffaf221e174051e9");
+    // Not a valid hash, has a non-hex-digit.
+    t_nohash!("_ZN3foo17hg5af221e174051e9E", "foo::hg5af221e174051e9");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_thinlto() {
+    // One element, no hash.
+    t!("_ZN3fooE.llvm.9D1C9369", "foo");
+    t!("_ZN3fooE.llvm.9D1C9369@@16", "foo");
+    t_nohash!(
+        "_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9",
+        "backtrace::foo"
+    );
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_llvm_ir_branch_labels() {
+    t!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut::haf9727c2edfbc47b.exit.i.i");
+    t_nohash!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut.exit.i.i");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_ignores_suffix_that_doesnt_look_like_a_symbol() {
+    t_err!("_ZN3fooE.llvm moocow");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn dont_panic() {
+    rust_demangle_c_test_harness::demangle("_ZN2222222222222222222222EE").to_string();
+    rust_demangle_c_test_harness::demangle("_ZN5*70527e27.ll34csaғE").to_string();
+    rust_demangle_c_test_harness::demangle("_ZN5*70527a54.ll34_$b.1E").to_string();
+    rust_demangle_c_test_harness::demangle(
+        "\
+         _ZN5~saäb4e\n\
+         2734cOsbE\n\
+         5usage20h)3\0\0\0\0\0\0\07e2734cOsbE\
+         ",
+    )
+    .to_string();
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn invalid_no_chop() {
+    t_err!("_ZNfooE");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn handle_assoc_types() {
+    t!("_ZN151_$LT$alloc..boxed..Box$LT$alloc..boxed..FnBox$LT$A$C$$u20$Output$u3d$R$GT$$u20$$u2b$$u20$$u27$a$GT$$u20$as$u20$core..ops..function..FnOnce$LT$A$GT$$GT$9call_once17h69e8f44b3723e1caE", "<alloc::boxed::Box<alloc::boxed::FnBox<A, Output=R> + 'a> as core::ops::function::FnOnce<A>>::call_once::h69e8f44b3723e1ca");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn handle_bang() {
+    t!(
+        "_ZN88_$LT$core..result..Result$LT$$u21$$C$$u20$E$GT$$u20$as$u20$std..process..Termination$GT$6report17hfc41d0da4a40b3e8E",
+        "<core::result::Result<!, E> as std::process::Termination>::report::hfc41d0da4a40b3e8"
+    );
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_utf8_idents() {
+    t_nohash!(
+        "_ZN11utf8_idents157_$u10e1$$u10d0$$u10ed$$u10db$$u10d4$$u10da$$u10d0$$u10d3$_$u10d2$$u10d4$$u10db$$u10e0$$u10d8$$u10d4$$u10da$$u10d8$_$u10e1$$u10d0$$u10d3$$u10d8$$u10da$$u10d8$17h21634fd5714000aaE",
+        "utf8_idents::საჭმელად_გემრიელი_სადილი"
+    );
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_issue_60925() {
+    t_nohash!(
+        "_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h059a991a004536adE",
+        "issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo"
+    );
+}
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/test-harness/tests/top_level.rs 1.4.0+dfsg-1/third-party/rust-demangle/test-harness/tests/top_level.rs
--- 1.3.1+dfsg-1/third-party/rust-demangle/test-harness/tests/top_level.rs	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/test-harness/tests/top_level.rs	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,255 @@
+//! Tests copied from `https://github.com/rust-lang/rustc-demangle`'s
+//! `src/lib.rs` at `fd906f850f90f6d4845c7b8219d218293e0ab3ed`.
+//!
+//! These are the only changes made to the tests:
+//! * `super::` paths -> `rust_demangle_c_test_harness::`
+//! * `#[ignore = "stack overflow"]` was added to tests that overflow the stack
+//! * `#[should_panic]` was added to tests that don't pass yet
+
+use rust_demangle_c_test_harness::{assert_contains, assert_ends_with};
+
+macro_rules! t {
+    ($a:expr, $b:expr) => {
+        assert!(ok($a, $b))
+    };
+}
+
+macro_rules! t_err {
+    ($a:expr) => {
+        assert!(ok_err($a))
+    };
+}
+
+macro_rules! t_nohash {
+    ($a:expr, $b:expr) => {{
+        assert_eq!(
+            format!("{:#}", rust_demangle_c_test_harness::demangle($a)),
+            $b
+        );
+    }};
+}
+
+fn ok(sym: &str, expected: &str) -> bool {
+    match rust_demangle_c_test_harness::try_demangle(sym) {
+        Ok(s) => {
+            if s.to_string() == expected {
+                true
+            } else {
+                println!("\n{}\n!=\n{}\n", s, expected);
+                false
+            }
+        }
+        Err(_) => {
+            println!("error demangling");
+            false
+        }
+    }
+}
+
+fn ok_err(sym: &str) -> bool {
+    match rust_demangle_c_test_harness::try_demangle(sym) {
+        Ok(_) => {
+            println!("succeeded in demangling");
+            false
+        }
+        Err(_) => rust_demangle_c_test_harness::demangle(sym).to_string() == sym,
+    }
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle() {
+    t_err!("test");
+    t!("_ZN4testE", "test");
+    t_err!("_ZN4test");
+    t!("_ZN4test1a2bcE", "test::a::bc");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_dollars() {
+    t!("_ZN4$RP$E", ")");
+    t!("_ZN8$RF$testE", "&test");
+    t!("_ZN8$BP$test4foobE", "*test::foob");
+    t!("_ZN9$u20$test4foobE", " test::foob");
+    t!("_ZN35Bar$LT$$u5b$u32$u3b$$u20$4$u5d$$GT$E", "Bar<[u32; 4]>");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_many_dollars() {
+    t!("_ZN13test$u20$test4foobE", "test test::foob");
+    t!("_ZN12test$BP$test4foobE", "test*test::foob");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_osx() {
+    t!(
+        "__ZN5alloc9allocator6Layout9for_value17h02a996811f781011E",
+        "alloc::allocator::Layout::for_value::h02a996811f781011"
+    );
+    t!("__ZN38_$LT$core..option..Option$LT$T$GT$$GT$6unwrap18_MSG_FILE_LINE_COL17haf7cb8d5824ee659E", "<core::option::Option<T>>::unwrap::_MSG_FILE_LINE_COL::haf7cb8d5824ee659");
+    t!("__ZN4core5slice89_$LT$impl$u20$core..iter..traits..IntoIterator$u20$for$u20$$RF$$u27$a$u20$$u5b$T$u5d$$GT$9into_iter17h450e234d27262170E", "core::slice::<impl core::iter::traits::IntoIterator for &'a [T]>::into_iter::h450e234d27262170");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_windows() {
+    t!("ZN4testE", "test");
+    t!("ZN13test$u20$test4foobE", "test test::foob");
+    t!("ZN12test$RF$test4foobE", "test&test::foob");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_elements_beginning_with_underscore() {
+    t!("_ZN13_$LT$test$GT$E", "<test>");
+    t!("_ZN28_$u7b$$u7b$closure$u7d$$u7d$E", "{{closure}}");
+    t!("_ZN15__STATIC_FMTSTRE", "__STATIC_FMTSTR");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_trait_impls() {
+    t!(
+        "_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE",
+        "<Test + 'static as foo::Bar<Test>>::bar"
+    );
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_without_hash() {
+    let s = "_ZN3foo17h05af221e174051e9E";
+    t!(s, "foo::h05af221e174051e9");
+    t_nohash!(s, "foo");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_without_hash_edgecases() {
+    // One element, no hash.
+    t_nohash!("_ZN3fooE", "foo");
+    // Two elements, no hash.
+    t_nohash!("_ZN3foo3barE", "foo::bar");
+    // Longer-than-normal hash.
+    t_nohash!("_ZN3foo20h05af221e174051e9abcE", "foo");
+    // Shorter-than-normal hash.
+    t_nohash!("_ZN3foo5h05afE", "foo");
+    // Valid hash, but not at the end.
+    t_nohash!("_ZN17h05af221e174051e93fooE", "h05af221e174051e9::foo");
+    // Not a valid hash, missing the 'h'.
+    t_nohash!("_ZN3foo16ffaf221e174051e9E", "foo::ffaf221e174051e9");
+    // Not a valid hash, has a non-hex-digit.
+    t_nohash!("_ZN3foo17hg5af221e174051e9E", "foo::hg5af221e174051e9");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_thinlto() {
+    // One element, no hash.
+    t!("_ZN3fooE.llvm.9D1C9369", "foo");
+    t!("_ZN3fooE.llvm.9D1C9369@@16", "foo");
+    t_nohash!(
+        "_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9",
+        "backtrace::foo"
+    );
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_llvm_ir_branch_labels() {
+    t!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut::haf9727c2edfbc47b.exit.i.i");
+    t_nohash!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut.exit.i.i");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_ignores_suffix_that_doesnt_look_like_a_symbol() {
+    t_err!("_ZN3fooE.llvm moocow");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn dont_panic() {
+    rust_demangle_c_test_harness::demangle("_ZN2222222222222222222222EE").to_string();
+    rust_demangle_c_test_harness::demangle("_ZN5*70527e27.ll34csaғE").to_string();
+    rust_demangle_c_test_harness::demangle("_ZN5*70527a54.ll34_$b.1E").to_string();
+    rust_demangle_c_test_harness::demangle(
+        "\
+         _ZN5~saäb4e\n\
+         2734cOsbE\n\
+         5usage20h)3\0\0\0\0\0\0\07e2734cOsbE\
+         ",
+    )
+    .to_string();
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn invalid_no_chop() {
+    t_err!("_ZNfooE");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn handle_assoc_types() {
+    t!("_ZN151_$LT$alloc..boxed..Box$LT$alloc..boxed..FnBox$LT$A$C$$u20$Output$u3d$R$GT$$u20$$u2b$$u20$$u27$a$GT$$u20$as$u20$core..ops..function..FnOnce$LT$A$GT$$GT$9call_once17h69e8f44b3723e1caE", "<alloc::boxed::Box<alloc::boxed::FnBox<A, Output=R> + 'a> as core::ops::function::FnOnce<A>>::call_once::h69e8f44b3723e1ca");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn handle_bang() {
+    t!(
+        "_ZN88_$LT$core..result..Result$LT$$u21$$C$$u20$E$GT$$u20$as$u20$std..process..Termination$GT$6report17hfc41d0da4a40b3e8E",
+        "<core::result::Result<!, E> as std::process::Termination>::report::hfc41d0da4a40b3e8"
+    );
+}
+
+// FIXME(eddyb) port recursion limits to C.
+#[ignore = "stack overflow"]
+#[test]
+fn limit_recursion() {
+    assert_contains!(
+        rust_demangle_c_test_harness::demangle("_RNvB_1a").to_string(),
+        "{recursion limit reached}"
+    );
+    assert_contains!(
+        rust_demangle_c_test_harness::demangle("_RMC0RB2_").to_string(),
+        "{recursion limit reached}"
+    );
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn limit_output() {
+    assert_ends_with!(
+        rust_demangle_c_test_harness::demangle("RYFG_FGyyEvRYFF_EvRYFFEvERLB_B_B_ERLRjB_B_B_")
+            .to_string(),
+        "{size limit reached}"
+    );
+    // NOTE(eddyb) somewhat reduced version of the above, effectively
+    // `<for<...> fn()>` with a larger number of lifetimes in `...`.
+    assert_ends_with!(
+        rust_demangle_c_test_harness::demangle("_RMC0FGZZZ_Eu").to_string(),
+        "{size limit reached}"
+    );
+}
diff -pruN 1.3.1+dfsg-1/third-party/rust-demangle/test-harness/tests/v0.rs 1.4.0+dfsg-1/third-party/rust-demangle/test-harness/tests/v0.rs
--- 1.3.1+dfsg-1/third-party/rust-demangle/test-harness/tests/v0.rs	1970-01-01 00:00:00.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/rust-demangle/test-harness/tests/v0.rs	2022-08-04 12:47:21.000000000 +0000
@@ -0,0 +1,328 @@
+//! Tests copied from `https://github.com/rust-lang/rustc-demangle`'s
+//! `src/v0.rs` at `fd906f850f90f6d4845c7b8219d218293e0ab3ed`.
+//!
+//! These are the only changes made to the tests:
+//! * `::` absolute paths -> `rust_demangle_c_test_harness::`
+//! * `#[cfg(unsupported_tests)]` was added to tests that couldn't compile
+//! * `#[ignore = "stack overflow"]` was added to tests that overflow the stack
+//! * `#[should_panic]` was added to tests that don't pass yet
+
+use rust_demangle_c_test_harness::assert_contains;
+
+macro_rules! t {
+    ($a:expr, $b:expr) => {{
+        assert_eq!(
+            format!("{}", rust_demangle_c_test_harness::demangle($a)),
+            $b
+        );
+    }};
+}
+macro_rules! t_nohash {
+    ($a:expr, $b:expr) => {{
+        assert_eq!(
+            format!("{:#}", rust_demangle_c_test_harness::demangle($a)),
+            $b
+        );
+    }};
+}
+macro_rules! t_nohash_type {
+    ($a:expr, $b:expr) => {
+        t_nohash!(concat!("_RMC0", $a), concat!("<", $b, ">"))
+    };
+}
+macro_rules! t_const {
+    ($mangled:expr, $value:expr) => {
+        t_nohash!(
+            concat!("_RIC0K", $mangled, "E"),
+            concat!("::<", $value, ">")
+        )
+    };
+}
+macro_rules! t_const_suffixed {
+    ($mangled:expr, $value:expr, $value_ty_suffix:expr) => {{
+        t_const!($mangled, $value);
+        t!(
+            concat!("_RIC0K", $mangled, "E"),
+            concat!("[0]::<", $value, $value_ty_suffix, ">")
+        );
+    }};
+}
+
+#[test]
+fn demangle_crate_with_leading_digit() {
+    t_nohash!("_RNvC6_123foo3bar", "123foo::bar");
+}
+
+#[test]
+fn demangle_utf8_idents() {
+    t_nohash!(
+        "_RNqCs4fqI2P2rA04_11utf8_identsu30____7hkackfecea1cbdathfdh9hlq6y",
+        "utf8_idents::საჭმელად_გემრიელი_სადილი"
+    );
+}
+
+#[test]
+fn demangle_closure() {
+    t_nohash!(
+        "_RNCNCNgCs6DXkGYLi8lr_2cc5spawn00B5_",
+        "cc::spawn::{closure#0}::{closure#0}"
+    );
+    t_nohash!(
+        "_RNCINkXs25_NgCsbmNqQUJIY6D_4core5sliceINyB9_4IterhENuNgNoBb_4iter8iterator8Iterator9rpositionNCNgNpB9_6memchr7memrchrs_0E0Bb_",
+        "<core::slice::Iter<u8> as core::iter::iterator::Iterator>::rposition::<core::slice::memchr::memrchr::{closure#1}>::{closure#0}"
+    );
+}
+
+#[test]
+fn demangle_dyn_trait() {
+    t_nohash!(
+        "_RINbNbCskIICzLVDPPb_5alloc5alloc8box_freeDINbNiB4_5boxed5FnBoxuEp6OutputuEL_ECs1iopQbuBiw2_3std",
+        "alloc::alloc::box_free::<dyn alloc::boxed::FnBox<(), Output = ()>>"
+    );
+}
+
+#[test]
+fn demangle_const_generics_preview() {
+    // NOTE(eddyb) this was hand-written, before rustc had working
+    // const generics support (but the mangling format did include them).
+    t_nohash_type!(
+        "INtC8arrayvec8ArrayVechKj7b_E",
+        "arrayvec::ArrayVec<u8, 123>"
+    );
+    t_const_suffixed!("j7b_", "123", "usize");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_min_const_generics() {
+    t_const!("p", "_");
+    t_const_suffixed!("hb_", "11", "u8");
+    t_const_suffixed!("off00ff00ff00ff00ff_", "0xff00ff00ff00ff00ff", "u128");
+    t_const_suffixed!("s98_", "152", "i16");
+    t_const_suffixed!("anb_", "-11", "i8");
+    t_const!("b0_", "false");
+    t_const!("b1_", "true");
+    t_const!("c76_", "'v'");
+    t_const!("c22_", r#"'"'"#);
+    t_const!("ca_", "'\\n'");
+    t_const!("c2202_", "'∂'");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_const_str() {
+    t_const!("e616263_", "{*\"abc\"}");
+    t_const!("e27_", r#"{*"'"}"#);
+    t_const!("e090a_", "{*\"\\t\\n\"}");
+    t_const!("ee28882c3bc_", "{*\"∂ü\"}");
+    t_const!(
+        "ee183a1e18390e183ade1839be18394e1839ae18390e183935fe18392e18394e1839b\
+          e183a0e18398e18394e1839ae183985fe183a1e18390e18393e18398e1839ae18398_",
+        "{*\"საჭმელად_გემრიელი_სადილი\"}"
+    );
+    t_const!(
+        "ef09f908af09fa688f09fa686f09f90ae20c2a720f09f90b6f09f9192e298\
+          95f09f94a520c2a720f09fa7a1f09f929bf09f929af09f9299f09f929c_",
+        "{*\"🐊🦈🦆🐮 § 🐶👒☕🔥 § 🧡💛💚💙💜\"}"
+    );
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+// NOTE(eddyb) this uses the same strings as `demangle_const_str` and should
+// be kept in sync with it - while a macro could be used to generate both
+// `str` and `&str` tests, from a single list of strings, this seems clearer.
+#[test]
+fn demangle_const_ref_str() {
+    t_const!("Re616263_", "\"abc\"");
+    t_const!("Re27_", r#""'""#);
+    t_const!("Re090a_", "\"\\t\\n\"");
+    t_const!("Ree28882c3bc_", "\"∂ü\"");
+    t_const!(
+        "Ree183a1e18390e183ade1839be18394e1839ae18390e183935fe18392e18394e1839b\
+           e183a0e18398e18394e1839ae183985fe183a1e18390e18393e18398e1839ae18398_",
+        "\"საჭმელად_გემრიელი_სადილი\""
+    );
+    t_const!(
+        "Ref09f908af09fa688f09fa686f09f90ae20c2a720f09f90b6f09f9192e298\
+           95f09f94a520c2a720f09fa7a1f09f929bf09f929af09f9299f09f929c_",
+        "\"🐊🦈🦆🐮 § 🐶👒☕🔥 § 🧡💛💚💙💜\""
+    );
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_const_ref() {
+    t_const!("Rp", "{&_}");
+    t_const!("Rh7b_", "{&123}");
+    t_const!("Rb0_", "{&false}");
+    t_const!("Rc58_", "{&'X'}");
+    t_const!("RRRh0_", "{&&&0}");
+    t_const!("RRRe_", "{&&\"\"}");
+    t_const!("QAE", "{&mut []}");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_const_array() {
+    t_const!("AE", "{[]}");
+    t_const!("Aj0_E", "{[0]}");
+    t_const!("Ah1_h2_h3_E", "{[1, 2, 3]}");
+    t_const!("ARe61_Re62_Re63_E", "{[\"a\", \"b\", \"c\"]}");
+    t_const!("AAh1_h2_EAh3_h4_EE", "{[[1, 2], [3, 4]]}");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_const_tuple() {
+    t_const!("TE", "{()}");
+    t_const!("Tj0_E", "{(0,)}");
+    t_const!("Th1_b0_E", "{(1, false)}");
+    t_const!(
+        "TRe616263_c78_RAh1_h2_h3_EE",
+        "{(\"abc\", 'x', &[1, 2, 3])}"
+    );
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_const_adt() {
+    t_const!(
+        "VNvINtNtC4core6option6OptionjE4NoneU",
+        "{core::option::Option::<usize>::None}"
+    );
+    t_const!(
+        "VNvINtNtC4core6option6OptionjE4SomeTj0_E",
+        "{core::option::Option::<usize>::Some(0)}"
+    );
+    t_const!(
+        "VNtC3foo3BarS1sRe616263_2chc78_5sliceRAh1_h2_h3_EE",
+        "{foo::Bar { s: \"abc\", ch: 'x', slice: &[1, 2, 3] }}"
+    );
+}
+
+#[test]
+fn demangle_exponential_explosion() {
+    // NOTE(eddyb) because of the prefix added by `t_nohash_type!` is
+    // 3 bytes long, `B2_` refers to the start of the type, not `B_`.
+    // 6 backrefs (`B8_E` through `B3_E`) result in 2^6 = 64 copies of `_`.
+    // Also, because the `p` (`_`) type is after all of the starts of the
+    // backrefs, it can be replaced with any other type, independently.
+    t_nohash_type!(
+        concat!("TTTTTT", "p", "B8_E", "B7_E", "B6_E", "B5_E", "B4_E", "B3_E"),
+        "((((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _)))), \
+         ((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _))))), \
+         (((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _)))), \
+         ((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _))))))"
+    );
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_thinlto() {
+    t_nohash!("_RC3foo.llvm.9D1C9369", "foo");
+    t_nohash!("_RC3foo.llvm.9D1C9369@@16", "foo");
+    t_nohash!("_RNvC9backtrace3foo.llvm.A5310EB9", "backtrace::foo");
+}
+
+// FIXME(eddyb) port the relevant functionality to C.
+#[should_panic]
+#[test]
+fn demangle_extra_suffix() {
+    // From alexcrichton/rustc-demangle#27:
+    t_nohash!(
+        "_RNvNtNtNtNtCs92dm3009vxr_4rand4rngs7adapter9reseeding4fork23FORK_HANDLER_REGISTERED.0.0",
+        "rand::rngs::adapter::reseeding::fork::FORK_HANDLER_REGISTERED.0.0"
+    );
+}
+
+// FIXME(eddyb) get this working with the `rust-demangle.c` test harness.
+#[cfg(unsupported_tests)]
+#[test]
+fn demangling_limits() {
+    // Stress tests found via fuzzing.
+
+    for sym in include_str!("v0-large-test-symbols/early-recursion-limit")
+        .lines()
+        .filter(|line| !line.is_empty() && !line.starts_with('#'))
+    {
+        assert_eq!(
+            super::demangle(sym).map(|_| ()),
+            Err(super::ParseError::RecursedTooDeep)
+        );
+    }
+
+    assert_contains!(
+        ::demangle(
+            "RIC20tRYIMYNRYFG05_EB5_B_B6_RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR\
+    RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRB_E",
+        )
+        .to_string(),
+        "{recursion limit reached}"
+    );
+}
+
+// FIXME(eddyb) get this working with the `rust-demangle.c` test harness.
+#[cfg(unsupported_tests)]
+#[test]
+fn recursion_limit_leaks() {
+    // NOTE(eddyb) this test checks that both paths and types support the
+    // recursion limit correctly, i.e. matching `push_depth` and `pop_depth`,
+    // and don't leak "recursion levels" and trip the limit.
+    // The test inputs are generated on the fly, using a repeated pattern,
+    // as hardcoding the actual strings would be too verbose.
+    // Also, `MAX_DEPTH` can be directly used, instead of assuming its value.
+    for &(sym_leaf, expected_leaf) in &[("p", "_"), ("Rp", "&_"), ("C1x", "x")] {
+        let mut sym = format!("_RIC0p");
+        let mut expected = format!("::<_");
+        for _ in 0..(super::MAX_DEPTH * 2) {
+            sym.push_str(sym_leaf);
+            expected.push_str(", ");
+            expected.push_str(expected_leaf);
+        }
+        sym.push('E');
+        expected.push('>');
+
+        t_nohash!(&sym, expected);
+    }
+}
+
+// FIXME(eddyb) port recursion limits to C.
+#[ignore = "stack overflow"]
+#[test]
+fn recursion_limit_backref_free_bypass() {
+    // NOTE(eddyb) this test checks that long symbols cannot bypass the
+    // recursion limit by not using backrefs, and cause a stack overflow.
+
+    // This value was chosen to be high enough that stack overflows were
+    // observed even with `cargo test --release`.
+    let depth = 100_000;
+
+    // In order to hide the long mangling from the initial "shallow" parse,
+    // it's nested in an identifier (crate name), preceding its use.
+    let mut sym = format!("_RIC{}", depth);
+    let backref_start = sym.len() - 2;
+    for _ in 0..depth {
+        sym.push('R');
+    }
+
+    // Write a backref to just after the length of the identifier.
+    sym.push('B');
+    sym.push(char::from_digit((backref_start - 1) as u32, 36).unwrap());
+    sym.push('_');
+
+    // Close the `I` at the start.
+    sym.push('E');
+
+    assert_contains!(
+        rust_demangle_c_test_harness::demangle(&sym).to_string(),
+        "{recursion limit reached}"
+    );
+}
diff -pruN 1.3.1+dfsg-1/third-party/tbb/include/oneapi/tbb/detail/_segment_table.h 1.4.0+dfsg-1/third-party/tbb/include/oneapi/tbb/detail/_segment_table.h
--- 1.3.1+dfsg-1/third-party/tbb/include/oneapi/tbb/detail/_segment_table.h	2022-07-01 08:15:37.000000000 +0000
+++ 1.4.0+dfsg-1/third-party/tbb/include/oneapi/tbb/detail/_segment_table.h	2022-08-04 12:47:21.000000000 +0000
@@ -542,8 +542,8 @@ protected:
     }
 
     segment_table_allocator_type my_segment_table_allocator;
-    std::atomic<segment_table_type> my_segment_table;
     atomic_segment my_embedded_table[pointers_per_embedded_table];
+    std::atomic<segment_table_type> my_segment_table;
     // Number of segments in first block
     std::atomic<size_type> my_first_block;
     // Number of elements in table
