Compare commits

..
1 Commits
Author SHA1 Message Date
Rui Ueyama 57f5c77174 ARC 2025-04-15 16:49:41 +09:00
19 changed files with 633 additions and 268 deletions

No files matched your search

+4 -7
View File
@@ -44,7 +44,7 @@
# features and behave exactly the same.
cmake_minimum_required(VERSION 3.14)
project(mold VERSION 2.38.0)
project(mold VERSION 2.37.1)
include(CMakeDependentOption)
include(CheckSymbolExists)
@@ -62,11 +62,7 @@ endif()
# Build mold itself using mold if -DMOLD_USE_MOLD=ON
option(MOLD_USE_MOLD "Use mold to build mold" OFF)
if(MOLD_USE_MOLD)
target_link_options(mold PRIVATE -fuse-ld=mold)
if(CMAKE_BUILD_TYPE MATCHES "Deb")
target_link_options(mold PRIVATE -Wl,--gdb-index)
endif()
target_link_options(mold PRIVATE -fuse-ld=mold -Wl,--gdb-index)
if(CMAKE_BUILD_TYPE MATCHES "^Rel")
target_link_options(mold PRIVATE -Wl,--gc-sections,--icf=safe)
@@ -315,7 +311,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
# on a multicore machine.
list(APPEND MOLD_ELF_TARGETS
X86_64 I386 ARM64LE ARM64BE ARM32 RV32LE RV32BE RV64LE RV64BE PPC32
PPC64V1 PPC64V2 S390X SPARC64 M68K SH4LE SH4BE LOONGARCH32 LOONGARCH64)
PPC64V1 PPC64V2 S390X SPARC64 M68K SH4LE SH4BE LOONGARCH32 LOONGARCH64 ARC)
list(APPEND MOLD_ELF_TEMPLATE_FILES
src/arch-arm64.cc
@@ -389,6 +385,7 @@ target_sources(mold PRIVATE
lib/perf.cc
lib/random.cc
lib/tar.cc
src/arch-arc.cc
src/arch-arm32.cc
src/arch-i386.cc
src/arch-m68k.cc
Executable
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash -x
set -e
apt-get install -y git bzip2 ninja-build pkg-config libglib2.0-dev python3-distutils-extra libpixman-1-dev
cd /
git clone --depth=1 https://github.com/foss-for-synopsys-dwc-arc-processors/qemu.git
mkdir /qemu/build
cd /qemu/build
../configure --static --extra-cflags=-Wno-error
ninja
ln -s /qemu/build/qemu-arc /qemu/build/qemu-arc64 /usr/local/bin
apt-get install -y libgmp-dev libmpfr-dev texinfo bison yacc flex
cd /
git clone --depth=1 https://github.com/foss-for-synopsys-dwc-arc-processors/binutils-gdb.git
cd /binutils-gdb
mkdir build
cd build
../configure --target=arc-linux
make -j$(nproc)
apt-get install -y mold
mkdir /build
cd /build
cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DMOLD_USE_MOLD=1 /mold
ninja
TRIPLE=arc-linux-gnu bash -x /mold/test/hello-dynamic.sh
+5 -13
View File
@@ -126,10 +126,10 @@ RUN mkdir /build && \
make install && \
rm -rf /build
# Build LLVM 20
# Build LLVM 19
RUN mkdir /build && \
cd /build && \
wget -O- --no-check-certificate --progress=dot:mega https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-20.1.3.tar.gz | tar xzf - --strip-components=1 && \
wget -O- --no-check-certificate --progress=dot:mega https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-19.1.7.tar.gz | tar xzf - --strip-components=1 && \
mkdir b && \
cd b && \
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS=clang ../llvm && \
@@ -195,22 +195,14 @@ dest=mold-$version-$arch-linux
# We use the timestamp of the last Git commit as the file timestamp
# for build artifacts.
timestamp=$(git log -1 --format=%ct)
timestamp="$(git log -1 --format=%ci)"
# Build mold in a container.
mkdir -p dist
# If the host is ARM64, `uname -m` in an ARM32 container running on it
# reports it not as ARM32 but as ARM64, which confuses BLAKE3's cmake script
# and erroneously enables NEON SIMD instructions. `setarch` can be used
# to change the output of `uname -m`.
[ $arch = arm ] && setarch='setarch linux32'
podman run --arch $arch -it --rm --userns=host --pids-limit=-1 --network=none \
--pull=never -v "$(pwd):/mold:ro" -v "$(pwd)/dist:/dist" $image \
$setarch bash -c "
-v "$(pwd):/mold:ro" -v "$(pwd)/dist:/dist" $image bash -c "
set -e
export SOURCE_DATE_EPOCH=$timestamp
mkdir /build
cd /build
cmake -DCMAKE_BUILD_TYPE=Release -DMOLD_MOSTLY_STATIC=1 -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ /mold
@@ -220,7 +212,7 @@ cmake -DMOLD_USE_MOLD=1 .
cmake --build . -j\$(nproc)
ctest --output-on-failure -j\$(nproc)
cmake --install . --prefix $dest --strip
find $dest -print | xargs touch --no-dereference --date=@$timestamp
find $dest -print | xargs touch --no-dereference --date='$timestamp'
find $dest -print | sort | tar -cf - --no-recursion --files-from=- | gzip -9nc > /dist/$dest.tar.gz
cp mold /dist
sha256sum /dist/$dest.tar.gz
+1 -1
View File
@@ -11,7 +11,7 @@ set -x
case "$ID" in
ubuntu | pop | linuxmint | debian | raspbian)
apt-get install -y qemu-user {gcc,g++}-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4}-linux-gnu {gcc,g++}-arm-linux-gnueabihf
apt-get install -y qemu-user {gcc,g++}-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,arc}-linux-gnu {gcc,g++}-arm-linux-gnueabihf
;;
*)
echo "Error: don't know anything about build dependencies on $ID-$VERSION_ID"
+285
View File
@@ -0,0 +1,285 @@
// r31 is blink. r30 is I believe a scratch register.
//
// Programs might use one of several branch, jump, and link instructions
// to control execution flow through direct and indirect function calls
// and branching. For function calling, the conditional branch-and-link
// instruction has a maximum branch range of ±1 MiB, and the target
// address is 32-bit-aligned. The unconditional branch-and-link format has
// a maximum branch range of ±16 MiB.
//
// https://github.com/foss-for-synopsys-dwc-arc-processors/arc-ABI-manual/blob/master/ARCv2_ABI.pdf
#include "mold.h"
namespace mold {
using E = ARC;
// An integer-like class for the mixed-endian encoding
class M32 {
public:
operator u32() const {
return (buf[1] << 24) | (buf[0] << 16) | (buf[3] << 8) | buf[2];
}
M32 &operator=(u32 x) {
buf[0] = x >> 16;
buf[1] = x >> 24;
buf[2] = x;
buf[3] = x >> 8;
return *this;
}
M32 &operator&=(u32 x) { return *this = *this & x; }
M32 &operator|=(u32 x) { return *this = *this | x; }
u8 buf[4];
};
static void write_disp7u(u8 *loc, u32 val) {
*(ul16 *)loc |= (bits(val, 6, 3) << 4) | bits(val, 2, 0);
}
static void write_disp9(u8 *loc, u32 val) {
*(M32 *)loc |= bits(val, 8, 0);
}
static void write_disp9ls(u8 *loc, u32 val) {
*(M32 *)loc |= (bits(val, 7, 0) << 16) | (bit(val, 8) << 15);
}
static void write_disp9s(u8 *loc, u32 val) {
*(ul16 *)loc |= bits(val, 8, 0);
}
static void write_disp10u(u8 *loc, u32 val) {
*(ul16 *)loc |= bits(val, 9, 0);
}
static void write_disp13s(u8 *loc, u32 val) {
*(ul16 *)loc |= bits(val, 12, 0);
}
static void write_disp21h(u8 *loc, u32 val) {
*(M32 *)loc |= (bits(val, 10, 1) << 17) | (bits(val, 20, 11) << 6);
}
static void write_disp21w(u8 *loc, u32 val) {
*(M32 *)loc |= (bits(val, 10, 2) << 18) | (bits(val, 20, 11) << 6);
}
static void write_disp25h(u8 *loc, u32 val) {
*(M32 *)loc |= (bits(val, 10, 1) << 17) | (bits(val, 20, 11) << 6) |
bits(val, 24, 21);
}
static void write_disp25w(u8 *loc, u32 val) {
*(M32 *)loc |= (bits(val, 10, 2) << 18) | (bits(val, 20, 11) << 6) |
bits(val, 24, 21);
}
static void write_disps9(u8 *loc, u32 val) {
*(ul16 *)loc |= bits(val, 10, 2);
}
static void write_disps12(u8 *loc, u32 val) {
*(M32 *)loc |= (bits(val, 5, 0) << 6) | bits(val, 11, 6);
}
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
static const ul16 insn[] = {
0x2730, 0x7f8b, 0, 0, // ld r11, [pcl,0]
0x2730, 0x7f8a, 0, 0, // ld r10, [pcl,0]
0x2020, 0x0280, // j [r10]
0, 0, // (address of GOTPLT)
};
memcpy(buf, insn, sizeof(insn));
*(M32 *)(buf + 4) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 4;
*(M32 *)(buf + 12) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr;
*(M32 *)(buf + 20) = ctx.gotplt->shdr.sh_addr;
}
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static const ul16 insn[] = {
0x2730, 0x7f8c, 0, 0, // ld r12, [pcl,0]
0x2021, 0x0300, // j.d [r12]
0x240a, 0x1fc0, // mov r12,pcl
};
memcpy(buf, insn, sizeof(insn));
*(M32 *)(buf + 4) = sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx);
}
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static const ul16 insn[] = {
0x2730, 0x7f8c, 0, 0, // ld r12, [pcl,0]
0x2021, 0x0300, // j.d [r12]
0x240a, 0x1fc0, // mov r12,pcl
};
memcpy(buf, insn, sizeof(insn));
*(M32 *)(buf + 4) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx);
}
template <>
void EhFrameSection<E>::apply_eh_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_NONE:
break;
case R_ARC_32:
*(ul32 *)loc = val;
break;
case R_ARC_32_PCREL:
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
template <>
void InputSection<E>::apply_reloc_alloc(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_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = get_addr() + rel.r_offset;
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
switch (rel.r_type) {
case R_ARC_32:
*(ul32 *)loc = S + A;
break;
case R_ARC_32_ME:
*(M32 *)loc = S + A;
break;
case R_ARC_S25H_PCREL:
case R_ARC_S25H_PCREL_PLT:
write_disp25h(loc, align_to(S + A - P, 2));
break;
case R_ARC_S25W_PCREL:
case R_ARC_S25W_PCREL_PLT:
write_disp25w(loc, align_to(S + A - P, 4));
break;
case R_ARC_PC32:
*(M32 *)loc = S + A - P;
break;
case R_ARC_32_PCREL:
*(ul32 *)loc = S + A - P;
break;
case R_ARC_GOTPC32:
*(M32 *)loc = GOT + G + A - P;
break;
case R_ARC_TLS_LE_32:
*(M32 *)loc = S + A - ctx.tp_addr;
break;
case R_ARC_TLS_IE_GOT:
*(M32 *)loc = sym.get_gottp_addr(ctx) + A - P;
break;
default:
unreachable();
}
}
}
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_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : (i64)rel.r_addend;
switch (rel.r_type) {
case R_ARC_32:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul32 *)loc = *val;
else
*(ul32 *)loc = S + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
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_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (sym.is_ifunc())
Error(ctx) << sym << ": GNU ifunc symbol is not supported on m68k";
switch (rel.r_type) {
case R_ARC_32:
break;
case R_ARC_32_ME:
scan_absrel(ctx, sym, rel);
break;
case R_ARC_S25H_PCREL:
case R_ARC_S25W_PCREL:
case R_ARC_32_PCREL:
case R_ARC_PC32:
scan_pcrel(ctx, sym, rel);
break;
case R_ARC_S25H_PCREL_PLT:
case R_ARC_S25W_PCREL_PLT:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_ARC_GOTPC32:
sym.flags |= NEEDS_GOT;
break;
case R_ARC_TLS_LE_32:
check_tlsle(ctx, sym, rel);
break;
case R_ARC_TLS_IE_GOT:
sym.flags |= NEEDS_GOTTP;
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
template <>
u64 get_eflags(Context<E> &ctx) {
return 0x406;
}
} // namespace mold
+6 -9
View File
@@ -587,7 +587,6 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
bool warn_shared_textrel = false;
bool error_unresolved_symbols = true;
std::optional<SeparateCodeKind> z_separate_code;
std::optional<bool> allow_shlib_undefined;
std::optional<bool> report_undefined;
std::optional<bool> z_relro;
std::optional<bool> z_dynamic_undefined_weak;
@@ -719,7 +718,8 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
<< " elf64briscv\n elf32lriscv\n elf32briscv\n"
<< " elf32ppc\n elf64ppc\n elf64lppc\n elf64_s390\n"
<< " elf64_sparc\n m68kelf\n shlelf_linux\n"
<< " shelf_linux\n elf64loongarch\n elf32loongarch";
<< " shelf_linux\n elf64loongarch\n elf32loongarch\n"
<< " arclinux";
version_shown = true;
} else if (read_arg("mllvm")) {
ctx.arg.plugin_opt.emplace_back(arg);
@@ -762,6 +762,8 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
ctx.arg.emulation = LOONGARCH64::name;
} else if (arg == "elf32loongarch") {
ctx.arg.emulation = LOONGARCH32::name;
} else if (arg == "arclinux") {
ctx.arg.emulation = ARC::name;
} else {
Fatal(ctx) << "unknown -m argument: " << arg;
}
@@ -1304,9 +1306,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
} else if (read_arg("filter") || read_arg("F")) {
ctx.arg.filter.push_back(arg);
} else if (read_flag("allow-shlib-undefined")) {
allow_shlib_undefined = true;
ctx.arg.allow_shlib_undefined = true;
} else if (read_flag("no-allow-shlib-undefined")) {
allow_shlib_undefined = false;
ctx.arg.allow_shlib_undefined = false;
} else if (read_arg("O")) {
} else if (read_flag("EB")) {
} else if (read_flag("EL")) {
@@ -1413,11 +1415,6 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
if (ctx.arg.pic)
ctx.arg.image_base = 0;
if (allow_shlib_undefined)
ctx.arg.allow_shlib_undefined = *allow_shlib_undefined;
else
ctx.arg.allow_shlib_undefined = ctx.arg.shared;
if (!report_undefined)
report_undefined = !ctx.arg.shared;
+69
View File
@@ -1038,4 +1038,73 @@ std::string rel_to_string<LOONGARCH32>(u32 r_type) {
return rel_to_string<LOONGARCH64>(r_type);
}
template <>
std::string rel_to_string<ARC>(u32 r_type) {
switch (r_type) {
CASE(R_ARC_8);
CASE(R_ARC_16);
CASE(R_ARC_24);
CASE(R_ARC_32);
CASE(R_ARC_N8);
CASE(R_ARC_N16);
CASE(R_ARC_N24);
CASE(R_ARC_N32);
CASE(R_ARC_SDA);
CASE(R_ARC_SECTOFF);
CASE(R_ARC_S21H_PCREL);
CASE(R_ARC_S21W_PCREL);
CASE(R_ARC_S25H_PCREL);
CASE(R_ARC_S25W_PCREL);
CASE(R_ARC_SDA32);
CASE(R_ARC_SDA_LDST);
CASE(R_ARC_SDA_LDST1);
CASE(R_ARC_SDA_LDST2);
CASE(R_ARC_SDA16_LD);
CASE(R_ARC_SDA16_LD1);
CASE(R_ARC_SDA16_LD2);
CASE(R_ARC_S13_PCREL);
CASE(R_ARC_W);
CASE(R_ARC_32_ME);
CASE(R_ARC_32_ME_S);
CASE(R_ARC_N32_ME);
CASE(R_ARC_SECTOFF_ME);
CASE(R_ARC_SDA32_ME);
CASE(R_ARC_W_ME);
CASE(R_ARC_SECTOFF_ME_1);
CASE(R_ARC_SECTOFF_ME_2);
CASE(R_ARC_SECTOFF_1);
CASE(R_ARC_SECTOFF_2);
CASE(R_ARC_SDA_12);
CASE(R_ARC_SDA16_ST2);
CASE(R_ARC_32_PCREL);
CASE(R_ARC_PC32);
CASE(R_ARC_GOT32);
CASE(R_ARC_GOTPC32);
CASE(R_ARC_PLT32);
CASE(R_ARC_COPY);
CASE(R_ARC_GLOB_DAT);
CASE(R_ARC_JMP_SLOT);
CASE(R_ARC_RELATIVE);
CASE(R_ARC_GOTOFF);
CASE(R_ARC_GOTPC);
CASE(R_ARC_S21W_PCREL_PLT);
CASE(R_ARC_S25H_PCREL_PLT);
CASE(R_ARC_JLI_SECTOFF);
CASE(R_ARC_TLS_DTPMOD);
CASE(R_ARC_TLS_TPOFF);
CASE(R_ARC_TLS_GD_GOT);
CASE(R_ARC_TLS_GD_LD);
CASE(R_ARC_TLS_GD_CALL);
CASE(R_ARC_TLS_IE_GOT);
CASE(R_ARC_TLS_DTPOFF);
CASE(R_ARC_TLS_DTPOFF_S9);
CASE(R_ARC_TLS_LE_S9);
CASE(R_ARC_TLS_LE_32);
CASE(R_ARC_S25W_PCREL_PLT);
CASE(R_ARC_S21H_PCREL_PLT);
CASE(R_ARC_NPS_CMEM16);
}
return unknown_type(r_type);
}
} // namespace mold
+92
View File
@@ -28,6 +28,7 @@ struct SH4LE;
struct SH4BE;
struct LOONGARCH64;
struct LOONGARCH32;
struct ARC;
template <typename E> struct ElfSym;
template <typename E> struct ElfShdr;
@@ -88,6 +89,7 @@ enum : u32 {
SHT_HIOS = 0x6fffffff,
SHT_X86_64_UNWIND = 0x70000001,
SHT_ARM_EXIDX = 0x70000001,
SHT_ARC_ATTRIBUTES = 0x70000001,
SHT_ARM_ATTRIBUTES = 0x70000003,
SHT_RISCV_ATTRIBUTES = 0x70000003,
SHT_LOUSER = 0x80000000,
@@ -235,6 +237,7 @@ enum : u32 {
EM_SPARC64 = 43,
EM_X86_64 = 62,
EM_AARCH64 = 183,
EM_ARC_COMPACT2 = 195,
EM_RISCV = 243,
EM_LOONGARCH = 258,
};
@@ -1362,6 +1365,71 @@ enum : u32 {
R_LARCH_TLS_DESC_PCREL20_S2 = 126,
};
enum : u32 {
R_ARC_8 = 1,
R_ARC_16 = 2,
R_ARC_24 = 3,
R_ARC_32 = 4,
R_ARC_N8 = 8,
R_ARC_N16 = 9,
R_ARC_N24 = 10,
R_ARC_N32 = 11,
R_ARC_SDA = 12,
R_ARC_SECTOFF = 13,
R_ARC_S21H_PCREL = 14,
R_ARC_S21W_PCREL = 15,
R_ARC_S25H_PCREL = 16,
R_ARC_S25W_PCREL = 17,
R_ARC_SDA32 = 18,
R_ARC_SDA_LDST = 19,
R_ARC_SDA_LDST1 = 20,
R_ARC_SDA_LDST2 = 21,
R_ARC_SDA16_LD = 22,
R_ARC_SDA16_LD1 = 23,
R_ARC_SDA16_LD2 = 24,
R_ARC_S13_PCREL = 25,
R_ARC_W = 26,
R_ARC_32_ME = 27,
R_ARC_32_ME_S = 105,
R_ARC_N32_ME = 28,
R_ARC_SECTOFF_ME = 29,
R_ARC_SDA32_ME = 30,
R_ARC_W_ME = 31,
R_ARC_SECTOFF_ME_1 = 41,
R_ARC_SECTOFF_ME_2 = 42,
R_ARC_SECTOFF_1 = 43,
R_ARC_SECTOFF_2 = 44,
R_ARC_SDA_12 = 45,
R_ARC_SDA16_ST2 = 48,
R_ARC_32_PCREL = 49,
R_ARC_PC32 = 50,
R_ARC_GOT32 = 59,
R_ARC_GOTPC32 = 51,
R_ARC_PLT32 = 52,
R_ARC_COPY = 53,
R_ARC_GLOB_DAT = 54,
R_ARC_JMP_SLOT = 55,
R_ARC_RELATIVE = 56,
R_ARC_GOTOFF = 57,
R_ARC_GOTPC = 58,
R_ARC_S21W_PCREL_PLT = 60,
R_ARC_S25H_PCREL_PLT = 61,
R_ARC_JLI_SECTOFF = 63,
R_ARC_TLS_DTPMOD = 66,
R_ARC_TLS_TPOFF = 68,
R_ARC_TLS_GD_GOT = 69,
R_ARC_TLS_GD_LD = 70,
R_ARC_TLS_GD_CALL = 71,
R_ARC_TLS_IE_GOT = 72,
R_ARC_TLS_DTPOFF = 67,
R_ARC_TLS_DTPOFF_S9 = 73,
R_ARC_TLS_LE_S9 = 74,
R_ARC_TLS_LE_32 = 75,
R_ARC_S25W_PCREL_PLT = 76,
R_ARC_S21H_PCREL_PLT = 77,
R_ARC_NPS_CMEM16 = 78,
};
// Returns true if a given relocation is of type used for direct
// function call.
template <typename E>
@@ -1825,6 +1893,7 @@ template <typename E> concept is_sh4le = std::same_as<E, SH4LE>;
template <typename E> concept is_sh4be = std::same_as<E, SH4BE>;
template <typename E> concept is_loongarch64 = std::same_as<E, LOONGARCH64>;
template <typename E> concept is_loongarch32 = std::same_as<E, LOONGARCH32>;
template <typename E> concept is_arc = std::same_as<E, ARC>;
template <typename E> concept is_x86 = is_x86_64<E> || is_i386<E>;
template <typename E> concept is_arm64 = is_arm64le<E> || is_arm64be<E>;
@@ -2228,4 +2297,27 @@ struct LOONGARCH32 {
static constexpr u32 R_FUNCALL[] = { R_LARCH_B26, R_LARCH_CALL36 };
};
struct ARC {
static constexpr std::string_view name = "arc";
static constexpr bool is_64 = false;
static constexpr bool is_le = true;
static constexpr bool is_rela = true;
static constexpr u32 page_size = 8192;
static constexpr u32 e_machine = EM_ARC_COMPACT2;
static constexpr u32 plt_hdr_size = 24;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u8 filler[] = { 0, 0, 0, 0 };
static constexpr u32 R_COPY = R_ARC_COPY;
static constexpr u32 R_GLOB_DAT = R_ARC_GLOB_DAT;
static constexpr u32 R_JUMP_SLOT = R_ARC_JMP_SLOT;
static constexpr u32 R_ABS = R_ARC_32;
static constexpr u32 R_RELATIVE = R_ARC_RELATIVE;
static constexpr u32 R_DTPOFF = R_ARC_TLS_DTPOFF;
static constexpr u32 R_TPOFF = R_ARC_TLS_TPOFF;
static constexpr u32 R_DTPMOD = R_ARC_TLS_DTPMOD;
static constexpr u32 R_FUNCALL[] = { R_ARC_S25W_PCREL_PLT };
};
} // namespace mold
+2
View File
@@ -160,6 +160,8 @@ static std::string_view get_elf_type(u8 *buf) {
return is_le ? SH4LE::name : SH4BE::name;
case EM_LOONGARCH:
return is_64 ? LOONGARCH64::name : LOONGARCH32::name;
case EM_ARC_COMPACT2:
return ARC::name;
default:
return "";
}
+12 -51
View File
@@ -317,6 +317,10 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
}
}
if constexpr (is_arc<E>)
if (shdr.sh_type == SHT_ARC_ATTRIBUTES)
continue;
switch (shdr.sh_type) {
case SHT_GROUP: {
// Get the signature of this section group.
@@ -999,7 +1003,6 @@ void ObjectFile<E>::resolve_symbols(Context<E> &ctx) {
}
std::scoped_lock lock(sym.mu);
if (get_rank(this, esym, !this->is_reachable) < get_rank(sym)) {
sym.file = this;
sym.set_input_section(isec);
@@ -1007,7 +1010,6 @@ void ObjectFile<E>::resolve_symbols(Context<E> &ctx) {
sym.sym_idx = i;
sym.ver_idx = ctx.default_version;
sym.is_weak = esym.is_weak();
sym.is_versioned_default = false;
}
}
}
@@ -1275,48 +1277,23 @@ void SharedFile<E>::parse(Context<E> &ctx) {
if (vers.empty() || esyms[i].is_undef())
ver = VER_NDX_GLOBAL;
else
ver = vers[i] & ~VERSYM_HIDDEN;
ver = (vers[i] & ~VERSYM_HIDDEN);
if (ver == VER_NDX_LOCAL)
continue;
std::string_view name = this->symbol_strtab.data() + esyms[i].st_name;
bool is_default = vers.empty() || !(vers[i] & VERSYM_HIDDEN);
this->elf_syms2.push_back(esyms[i]);
this->versyms.push_back(ver);
std::string_view name = this->symbol_strtab.data() + esyms[i].st_name;
auto get_versioned_sym = [&] {
std::string_view key = save_string(
ctx, std::string(name) + "@" + std::string(version_strings[ver]));
return get_symbol(ctx, key, name);
};
// Symbol resolution involving symbol versioning is tricky because one
// symbol can be resolved with two different identifiers. Among
// symbols with the same name but different versions, one of them is
// always marked as the "default" one. This symbol is often denoted
// with two atsigns as `foo@@VERSION` and can be referred to either
// as `foo` or `foo@VERSION`. No other symbols have two names like that.
//
// On contrary, a versioned non-default symbol can be referred only
// with an explicit version suffix, e.g., `foo@VERSION`.
//
// Here is how we resolve versioned default symbols. We resolve `foo`
// and `foo@VERSION` as usual, but with information to forward
// references to `foo@VERSION` to `foo`. After name resolution, we
// visit all symbol references to redirect `foo@VERSION` to `foo`.
if (vers.empty() || ver == VER_NDX_GLOBAL) {
// Unversioned symbol
if (is_default) {
this->symbols.push_back(get_symbol(ctx, name));
this->symbols2.push_back(nullptr);
} else if (vers[i] & VERSYM_HIDDEN) {
// Versioned non-default symbol
this->symbols.push_back(get_versioned_sym());
this->symbols2.push_back(nullptr);
} else {
// Versioned default symbol
this->symbols.push_back(get_symbol(ctx, name));
this->symbols2.push_back(get_versioned_sym());
std::string_view mangled_name = save_string(
ctx, std::string(name) + "@" + std::string(version_strings[ver]));
this->symbols.push_back(get_symbol(ctx, mangled_name, name));
}
}
@@ -1423,22 +1400,6 @@ void SharedFile<E>::resolve_symbols(Context<E> &ctx) {
sym.sym_idx = i;
sym.ver_idx = versyms[i];
sym.is_weak = true;
sym.is_versioned_default = false;
}
// A symbol with the default version is a special case because, unlike
// other symbols, the symbol can be referred by two names, `foo` and
// `foo@VERSION`. Here, we resolve `foo@VERSOIN` as a proxy of `foo`.
Symbol<E> *sym2 = this->symbols2[i];
if (sym2 && sym2 != &sym) {
std::scoped_lock lock2(sym2->mu);
if (get_rank(this, esym, false) < get_rank(*sym2)) {
sym2->file = this;
sym2->origin = (uintptr_t)&sym;
sym2->sym_idx = i;
sym2->is_versioned_default = true;
}
}
}
}
+9 -9
View File
@@ -44,8 +44,8 @@ static ObjectFile<E> *new_object_file(Context<E> &ctx, ReaderContext &rctx,
ObjectFile<E> *file = new ObjectFile<E>(ctx, mf, archive_name);
ctx.obj_pool.emplace_back(file);
file->priority = ctx.file_priority++;
file->as_needed =
rctx.in_lib || (!archive_name.empty() && !rctx.whole_archive);
file->is_reachable =
!rctx.in_lib && (archive_name.empty() || rctx.whole_archive);
rctx.tg->run([file, &ctx] { file->parse(ctx); });
if (ctx.arg.trace)
@@ -65,8 +65,8 @@ static ObjectFile<E> *new_lto_obj(Context<E> &ctx, ReaderContext &rctx,
ObjectFile<E> *file = read_lto_object(ctx, mf);
file->priority = ctx.file_priority++;
file->archive_name = archive_name;
file->as_needed =
rctx.in_lib || (!archive_name.empty() && !rctx.whole_archive);
file->is_reachable =
!rctx.in_lib && (archive_name.empty() || rctx.whole_archive);
if (ctx.arg.trace)
Out(ctx) << "trace: " << *file;
@@ -81,7 +81,7 @@ new_shared_file(Context<E> &ctx, ReaderContext &rctx, MappedFile *mf) {
SharedFile<E> *file = new SharedFile<E>(ctx, mf);
ctx.dso_pool.emplace_back(file);
file->priority = ctx.file_priority++;
file->as_needed = rctx.as_needed;
file->is_reachable = !rctx.as_needed;
rctx.tg->run([file, &ctx] { file->parse(ctx); });
if (ctx.arg.trace)
@@ -496,7 +496,7 @@ int mold_main(int argc, char **argv) {
// create as few segments as possible.
sort_output_sections(ctx);
if (ctx.gnu_debuglink)
if (!ctx.arg.separate_debug_file.empty())
separate_debug_sections(ctx);
// Compute sizes of output sections while assigning offsets
@@ -612,7 +612,7 @@ int mold_main(int argc, char **argv) {
// .gdb_index's contents cannot be constructed before applying
// relocations to other debug sections. We have relocated debug
// sections now, so write the .gdb_index section.
if (ctx.gdb_index && !ctx.gnu_debuglink)
if (ctx.gdb_index && ctx.arg.separate_debug_file.empty())
write_gdb_index(ctx);
// .note.gnu.build-id section contains a cryptographic hash of the
@@ -621,7 +621,7 @@ int mold_main(int argc, char **argv) {
if (ctx.buildid)
write_build_id(ctx);
if (ctx.gnu_debuglink)
if (!ctx.arg.separate_debug_file.empty())
write_gnu_debuglink(ctx);
t_copy.stop();
@@ -642,7 +642,7 @@ int mold_main(int argc, char **argv) {
if (ctx.arg.print_map)
print_map(ctx);
if (ctx.gnu_debuglink)
if (!ctx.arg.separate_debug_file.empty())
write_separate_debug_file(ctx);
// Show stats numbers
+7 -6
View File
@@ -1353,7 +1353,6 @@ public:
std::string_view shstrtab;
std::string_view symbol_strtab;
bool as_needed = false;
bool has_init_array = false;
bool has_ctors = false;
@@ -1488,7 +1487,6 @@ public:
std::string soname;
std::vector<std::string_view> version_strings;
std::vector<Symbol<E> *> symbols2;
std::vector<ElfSym<E>> elf_syms2;
private:
@@ -1810,6 +1808,12 @@ public:
template <> u64 get_eflags(Context<PPC64V2> &ctx);
//
// arch-arc.cc
//
template <> u64 get_eflags(Context<ARC> &ctx);
//
// main.cc
//
@@ -1930,7 +1934,7 @@ struct ContextExtras<SPARC64> {
template <typename E>
struct Context {
Context() {
arg.entry = get_symbol(*this, "_start");
arg.entry = get_symbol(*this, is_arc<E> ? "__start" : "_start");
arg.fini = get_symbol(*this, "_fini");
arg.init = get_symbol(*this, "_init");
@@ -2388,9 +2392,6 @@ public:
bool is_traced : 1 = false; // for --trace-symbol
bool is_wrapped : 1 = false; // for --wrap
// For symbols with default symbol version, e.g. foo@@VERSION.
bool is_versioned_default : 1 = false;
// If a symbol can be resolved to a symbol in a different ELF file at
// runtime, `is_imported` is true. If a symbol is a dynamic symbol and
// can be used by other ELF file at runtime, `is_exported` is true.
+100 -111
View File
@@ -56,6 +56,8 @@ int redo_main(Context<E> &ctx, int argc, char **argv) {
return mold_main<LOONGARCH32>(argc, argv);
if (target == LOONGARCH64::name)
return mold_main<LOONGARCH64>(argc, argv);
if (target == ARC::name)
return mold_main<ARC>(argc, argv);
abort();
}
@@ -215,14 +217,14 @@ static void mark_live_objects(Context<E> &ctx) {
}
std::vector<InputFile<E> *> roots;
append(roots, ctx.objs);
append(roots, ctx.dsos);
for (InputFile<E> *file : roots)
if (!file->as_needed)
file->is_reachable = true;
for (InputFile<E> *file : ctx.objs)
if (file->is_reachable)
roots.push_back(file);
std::erase_if(roots, [](InputFile<E> *file) { return !file->is_reachable; });
for (InputFile<E> *file : ctx.dsos)
if (file->is_reachable)
roots.push_back(file);
tbb::parallel_for_each(roots, [&](InputFile<E> *file,
tbb::feeder<InputFile<E> *> &feeder) {
@@ -247,14 +249,25 @@ static void mark_live_objects(Context<E> &ctx) {
// `VER1`. If it does, that's the symbol we are looking for.
template <typename E>
static void resolve_default_symver(Context<E> &ctx) {
std::vector<InputFile<E> *> files;
append(files, ctx.objs);
append(files, ctx.dsos);
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
const ElfSym<E> &esym = file->elf_syms[i];
Symbol<E> &sym = *file->symbols[i];
tbb::parallel_for_each(files, [](InputFile<E> *file) {
for (Symbol<E> *&sym : file->get_global_syms())
if (sym->is_versioned_default)
sym = (Symbol<E> *)sym->origin;
if (!sym.file && esym.is_undef() &&
file->has_symver[i - file->first_global]) {
std::string_view str = file->symbol_strtab.data() + esym.st_name;
i64 pos = str.find('@');
assert(pos != str.npos);
std::string_view name = str.substr(0, pos);
std::string_view ver = str.substr(pos + 1);
Symbol<E> *sym2 = get_symbol(ctx, name);
if (sym2->file && sym2->file->is_dso && sym2->get_version() == ver)
file->symbols[i] = sym2;
}
}
});
}
@@ -299,68 +312,69 @@ void resolve_symbols(Context<E> &ctx) {
resolve_default_symver(ctx);
mark_live_objects(ctx);
// Now that we know the exact set of input files that are to be
// included in the output file, we want to redo symbol resolution.
// This is because symbols defined by object files in archive files
// may have risen as a result of mark_live_objects().
//
// To redo symbol resolution, we want to clear the state first.
clear_symbols(ctx);
// COMDAT elimination needs to happen exactly here.
//
// It needs to be after archive extraction, otherwise we might
// assign COMDAT leader to an archive member that is not supposed to
// be extracted.
//
// It needs to happen before the final symbol resolution, otherwise
// we could eliminate a symbol that is already resolved to and cause
// dangling references.
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
if (file->is_reachable) {
for (ComdatGroupRef<E> &ref : file->comdat_groups)
update_minimum(ref.group->owner, file->priority);
for (ComdatGroup *g : file->lto_comdat_groups)
if (g)
update_minimum(g->owner, file->priority);
}
});
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
if (file->is_reachable)
for (ComdatGroupRef<E> &ref : file->comdat_groups)
if (ref.group->owner != file->priority)
for (u32 i : ref.members)
if (InputSection<E> *isec = file->sections[i].get())
isec->is_alive = false;
});
// Redo symbol resolution
tbb::parallel_for_each(files, [&](InputFile<E> *file) {
if (file->is_reachable)
file->resolve_symbols(ctx);
});
// Symbols with hidden visibility need to be resolved within the
// output file. If a hidden symbol was resolved to a DSO, we'll redo
// symbol resolution from scratch with the flag to skip that symbol
// next time. This should be rare.
std::atomic_bool redo = false;
std::atomic_bool flag = false;
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
if (file->is_reachable) {
for (Symbol<E> *sym : file->symbols) {
if (sym->file == file && sym->visibility == STV_HIDDEN) {
sym->skip_dso = true;
redo = true;
flag = true;
}
}
}
});
if (!redo)
break;
if (!flag)
return;
clear_symbols(ctx);
}
// Now that we know the exact set of input files that are to be
// included in the output file, we want to redo symbol resolution.
// This is because symbols defined by object files in archive files
// may have risen as a result of mark_live_objects().
//
// To redo symbol resolution, we want to clear the state first.
clear_symbols(ctx);
// COMDAT elimination needs to happen exactly here.
//
// It needs to be after archive extraction, otherwise we might
// assign COMDAT leader to an archive member that is not supposed to
// be extracted.
//
// It needs to happen before the final symbol resolution, otherwise
// we could eliminate a symbol that is already resolved to and cause
// dangling references.
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
if (file->is_reachable) {
for (ComdatGroupRef<E> &ref : file->comdat_groups)
update_minimum(ref.group->owner, file->priority);
for (ComdatGroup *g : file->lto_comdat_groups)
if (g)
update_minimum(g->owner, file->priority);
}
});
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
if (file->is_reachable)
for (ComdatGroupRef<E> &ref : file->comdat_groups)
if (ref.group->owner != file->priority)
for (u32 i : ref.members)
if (InputSection<E> *isec = file->sections[i].get())
isec->is_alive = false;
});
// Redo symbol resolution
tbb::parallel_for_each(files, [&](InputFile<E> *file) {
if (file->is_reachable)
file->resolve_symbols(ctx);
});
}
// Do link-time optimization. We pass all IR object files to the compiler
@@ -1113,34 +1127,17 @@ void check_shlib_undefined(Context<E> &ctx) {
return false;
};
// We want to skip a file that (either directly or indirectly) depends
// on another .so file we know nothing about. This is because missing
// symbols might be provided by that unknown file.
//
// We need recursion for indirect dependencies.
std::unordered_map<std::string_view, std::vector<std::string_view>> deps;
// Obtain a list of known shared library names.
std::unordered_set<std::string_view> sonames;
for (std::unique_ptr<SharedFile<E>> &file : ctx.dso_pool)
deps[file->soname] = file->get_dt_needed(ctx);
std::function<bool(std::string_view, i64)> can_check;
can_check = [&](std::string_view soname, i64 depth) -> bool {
if (depth == deps.size() + 1)
Fatal(ctx) << "--no-allow-shlib-defined: mutually-recursive .so detected: "
<< soname;
auto it = deps.find(soname);
if (it == deps.end())
return false;
for (std::string_view needed : it->second)
if (!can_check(needed, depth + 1))
return false;
return true;
};
sonames.insert(file->soname);
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
// Skip the file if it depends on a file that we know nothing about.
if (!can_check(file->soname, 0))
return;
// This is because missing symbols may be provided by that unknown file.
for (std::string_view needed : file->get_dt_needed(ctx))
if (!sonames.contains(needed))
return;
// Check if all undefined symbols have been resolved.
for (i64 i = 0; i < file->elf_syms.size(); i++) {
@@ -1158,7 +1155,7 @@ void check_shlib_undefined(Context<E> &ctx) {
// SharedFile<E>::mark_live_objects just for this pass. Therefore,
// remove unneeded DSOs from the list now.
for (SharedFile<E> *file : ctx.dsos)
file->is_reachable = !file->as_needed;
file->is_reachable = false;
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->get_global_syms())
@@ -1174,6 +1171,10 @@ template <typename E>
void check_symbol_types(Context<E> &ctx) {
Timer t(ctx, "check_symbol_types");
std::vector<InputFile<E> *> files;
append(files, ctx.objs);
append(files, ctx.dsos);
auto canonicalize = [](u32 ty) -> u32 {
if (ty == STT_GNU_IFUNC)
return STT_FUNC;
@@ -1182,35 +1183,23 @@ void check_symbol_types(Context<E> &ctx) {
return ty;
};
auto check = [&](InputFile<E> &file, Symbol<E> &sym,
const ElfSym<E> &esym1, const ElfSym<E> &esym2) {
if (sym.file && sym.file != &file &&
esym1.st_type != STT_NOTYPE && esym2.st_type != STT_NOTYPE &&
canonicalize(esym1.st_type) != canonicalize(esym2.st_type)) {
Warn(ctx) << "symbol type mismatch: " << sym << '\n'
<< ">>> defined in " << *sym.file << " as "
<< stt_to_string<E>(esym1.st_type) << '\n'
<< ">>> defined in " << file << " as "
<< stt_to_string<E>(esym2.st_type);
}
};
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (i64 i = file->first_global; i < file->elf_syms.size(); i++)
if (Symbol<E> *sym = file->symbols[i];
sym->file && sym->file != file)
check(*file, *sym, sym->esym(), file->elf_syms[i]);
});
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
tbb::parallel_for_each(files.begin(), files.end(), [&](InputFile<E> *file) {
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
if (Symbol<E> *sym = file->symbols[i];
sym->file && sym->file != file)
check(*file, *sym, sym->esym(), file->elf_syms[i]);
Symbol<E> &sym = *file->symbols[i];
if (!sym.file || sym.file == file)
continue;
if (Symbol<E> *sym = file->symbols2[i])
if (sym->file && sym->file != file)
check(*file, *sym, sym->esym(), file->elf_syms[i]);
const ElfSym<E> &esym1 = sym.esym();
const ElfSym<E> &esym2 = file->elf_syms[i];
if (esym1.st_type != STT_NOTYPE && esym2.st_type != STT_NOTYPE &&
canonicalize(esym1.st_type) != canonicalize(esym2.st_type)) {
Warn(ctx) << "symbol type mismatch: " << sym << '\n'
<< ">>> defined in " << *sym.file << " as "
<< stt_to_string<E>(esym1.st_type) << '\n'
<< ">>> defined in " << *file << " as "
<< stt_to_string<E>(esym2.st_type);
}
}
});
}
+1 -1
View File
@@ -162,7 +162,7 @@ u64 get_tp_addr(const ElfPhdr<E> &phdr) {
// and %a0/%a1 on s390x) refers to past the end of the TLS block for
// historical reasons. TLVs are accessed with negative offsets from TP.
return align_to(phdr.p_vaddr + phdr.p_memsz, phdr.p_align);
} else if constexpr (is_arm<E> || is_sh4<E>) {
} else if constexpr (is_arm<E> || is_sh4<E> || is_arc<E>) {
// On ARM and SH4, the runtime appends two words at the beginning
// of TLV template image when copying TLVs to the TLS block, so we need
// to offset it.
+7 -2
View File
@@ -31,7 +31,7 @@ endif()
if(MOLD_ENABLE_QEMU_TESTS)
list(APPEND QEMU_ARCHS
x86_64 i386 arm aarch64 aarch64_be ppc ppc64 ppc64le sparc64 sh4 sh4eb
s390x riscv64 riscv32 m68k loongarch64)
s390x riscv64 riscv32 m68k loongarch64 arc)
LIST(APPEND TRIPLES
x86_64-linux-gnu
@@ -49,7 +49,8 @@ if(MOLD_ENABLE_QEMU_TESTS)
sh4aeb-linux-gnu
riscv32-linux-gnu
m68k-linux-gnu
loongarch64-linux-gnu)
loongarch64-linux-gnu
arc-linux-gnu)
foreach(ARCH IN LISTS QEMU_ARCHS)
find_program(HAS_qemu-${ARCH} qemu-${ARCH})
@@ -184,3 +185,7 @@ endif()
if(${MACHINE} STREQUAL "loongarch64" OR (HAS_qemu-loongarch64 AND HAS_loongarch64-linux-gnu-gcc))
add_target(loongarch64 loongarch64-linux-gnu)
endif()
if(${MACHINE} STREQUAL "arc" OR (HAS_qemu-loongarch64 AND HAS_arc-linux-gnu-gcc))
add_target(arc arc-linux-gnu)
endif()
+2 -2
View File
@@ -22,8 +22,8 @@ void bar();
int main() { bar(); }
EOF
$CC -B. -o $t/exe1 $t/c.o $t/b.so -Wl,--allow-shlib-undefined
$CC -B. -o $t/exe1 $t/c.o $t/b.so
readelf --dynamic $t/exe1 | grep 'Dependency audit library:..*/a.so'
$CC -B. -o $t/exe2 $t/c.o $t/b.so -Wl,--depaudit=foo -Wl,--allow-shlib-undefined
$CC -B. -o $t/exe2 $t/c.o $t/b.so -Wl,--depaudit=foo
readelf --dynamic $t/exe2 | grep 'Dependency audit library:..*foo:.*/a.so'
-29
View File
@@ -1,29 +0,0 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -B. -shared -fPIC -o $t/libfoo.so -xc -
void foo() {}
EOF
cat <<EOF | $CC -B. -shared -fPIC -o $t/libbar.so -xc - -L$t -lfoo
void foo();
void bar() { foo(); }
EOF
cat <<EOF | $CC -B. -shared -fPIC -o $t/libbaz.so -xc - -L$t -lbar
void bar();
void baz() { bar(); }
EOF
cat <<EOF | $CC -c -o $t/a.o -c -xc -
int baz();
int main() { baz(); }
EOF
mv $t/libfoo.so $t/libfoo.so.bak
echo | $CC -B. -shared -fPIC -o $t/libfoo.so -xc -
$CC -B. -o $t/exe1 $t/a.o -Wl,--no-allow-shlib-undefined -L$t -lbar -lbaz
not $CC -B. -o $t/exe2 $t/a.o -Wl,--no-allow-shlib-undefined \
-L$t -lfoo -lbar -lbaz |& grep 'no-allow-shlib-undefined: undefined symbol: foo'
-26
View File
@@ -1,26 +0,0 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc - -fPIC
void foo() {}
EOF
cat <<EOF | $CC -o $t/b.o -c -xc - -fPIC
__asm__(".symver foo_,foo@@VERSION");
void foo_() {}
EOF
cat <<EOF > $t/c.version
VERSION { global: foo; local: *; };
EOF
$CC -B. -shared -o $t/libfoo.so $t/a.o
$CC -B. -shared -o $t/libbar.so $t/b.o -Wl,--version-script=$t/c.version
cat <<EOF | $CC -o $t/d.o -c -xc -
void foo();
int main() { foo(); }
__asm__(".symver foo,foo@VERSION");
EOF
$CC -B. -o $t/exe $t/d.o -L$t -lfoo -lbar
+2 -1
View File
@@ -9,4 +9,5 @@ int main() {
}
EOF
$CC -B. -shared -o $t/b.so $t/a.o |& grep 'warning: symbol type mismatch: times'
$CC -B. -shared -o $t/a.so $t/a.o |&
grep 'warning: symbol type mismatch: times'