Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions scripts/003-normalize-llvm-psp-archives.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash

set -euo pipefail

# Normalize the core PSPSDK archives before psp-packages and psplink build
# against them. The final normalization step runs again after packages install
# their own archives.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
"${SCRIPT_DIR}/006-normalize-llvm-psp-archives.sh"
91 changes: 91 additions & 0 deletions scripts/004-psplinkusb-extra.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,97 @@ else
cd $REPO_FOLDER && git fetch origin && git reset --hard origin/${BRANCH_NAME} || { exit 1; }
fi

## The clang-built SDK archives are normalized to rustc-compatible double-float
## metadata. Keep psplinkusb's PSP-side object metadata consistent with those
## archives so BFD ld does not warn while linking these helper PRX/ELF files.
## Do this after compilation instead of changing CFLAGS, because psplinkusb
## contains real float/double code and should keep its original codegen.
NORMALIZE_OBJECTS="$PWD/normalize-psp-object-metadata.sh"
cat > "$NORMALIZE_OBJECTS" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

[[ "$#" -eq 0 ]] && exit 0

perl - "$@" <<'PERL'
use strict;
use warnings;

for my $path (@ARGV) {
open(my $fh, "+<:raw", $path) or die "open $path: $!";
local $/;
my $data = <$fh>;
next unless length($data) >= 52;
next unless substr($data, 0, 4) eq "\x7fELF";
next unless unpack("C", substr($data, 4, 1)) == 1; # ELFCLASS32
next unless unpack("C", substr($data, 5, 1)) == 1; # ELFDATA2LSB
next unless unpack("v", substr($data, 18, 2)) == 8; # EM_MIPS

my $flags = unpack("V", substr($data, 36, 4));
substr($data, 36, 4) = pack("V", $flags & 0xf000ffff);

my $shoff = unpack("V", substr($data, 32, 4));
my $shentsize = unpack("v", substr($data, 46, 2));
my $shnum = unpack("v", substr($data, 48, 2));
my $shstrndx = unpack("v", substr($data, 50, 2));
if ($shoff && $shentsize && $shstrndx < $shnum) {
my $shstr = $shoff + $shstrndx * $shentsize;
if ($shstr + 24 <= length($data)) {
my $shstr_off = unpack("V", substr($data, $shstr + 16, 4));
my $shstr_size = unpack("V", substr($data, $shstr + 20, 4));
my $shstr_end = $shstr_off + $shstr_size;
if ($shstr_end <= length($data)) {
for (my $i = 0; $i < $shnum; $i++) {
my $sh = $shoff + $i * $shentsize;
next if $sh + 24 > length($data);
my $name_off = unpack("V", substr($data, $sh, 4));
my $name_start = $shstr_off + $name_off;
next if $name_start >= $shstr_end;
my $name_end = index($data, "\0", $name_start);
next if $name_end < 0 || $name_end > $shstr_end;
my $name = substr($data, $name_start, $name_end - $name_start);

if ($name eq ".MIPS.abiflags") {
my $off = unpack("V", substr($data, $sh + 16, 4));
my $size = unpack("V", substr($data, $sh + 20, 4));
next if $size < 24 || $off + $size > length($data);
substr($data, $off + 7, 1) = "\x01";
substr($data, $off + 8, 4) = pack("V", 0);
substr($data, $off + 12, 4) = pack("V", 0);
substr($data, $off + 16, 4) = pack("V", 1);
}

if ($name eq ".gnu.attributes") {
my $off = unpack("V", substr($data, $sh + 16, 4));
my $size = unpack("V", substr($data, $sh + 20, 4));
next if $off + $size > length($data);
my $attrs = substr($data, $off, $size);
$attrs =~ s/\x04\x02/\x04\x01/g;
substr($data, $off, $size) = $attrs;
}
}
}
}
}

seek($fh, 0, 0) or die "seek $path: $!";
print {$fh} $data or die "write $path: $!";
truncate($fh, length($data)) or die "truncate $path: $!";
close($fh) or die "close $path: $!";
}
PERL
EOF
chmod +x "$NORMALIZE_OBJECTS"
export PSPLINKUSB_NORMALIZE_OBJECTS="$NORMALIZE_OBJECTS"

while IFS= read -r MAKEFILE; do
perl -0pi -e 's/(\nPSPSDK=\$\(shell psp-config --pspsdk-path\)\ninclude \$\(PSPSDK\)\/lib\/build[^ \n]*\.mak\n)/$1\n.PHONY: normalize-psp-objects\n\$(TARGET).elf: | normalize-psp-objects\nnormalize-psp-objects: \$(OBJS) \$(EXPORT_OBJ)\n\t\$(PSPLINKUSB_NORMALIZE_OBJECTS) \$(OBJS) \$(EXPORT_OBJ)\n/s' "$MAKEFILE"
done < <(find . -name Makefile -type f -exec grep -l '\$(PSPSDK)/lib/build' {} +)

for MAKEFILE in libpsplink/Makefile libpsplink_driver/Makefile libusbhostfs/Makefile libusbhostfs_driver/Makefile; do
perl -0pi -e 's/\n\$\((TARGET)\): \$\((OBJS)\)/\n.PHONY: normalize-psp-objects\nnormalize-psp-objects: \$(OBJS)\n\t\$(PSPLINKUSB_NORMALIZE_OBJECTS) \$(OBJS)\n\n\$(TARGET): \$(OBJS) | normalize-psp-objects/' "$MAKEFILE"
done

## Determine the maximum number of processes that Make can work with.
PROC_NR=$(getconf _NPROCESSORS_ONLN)
OSVER=$(uname)
Expand Down
209 changes: 209 additions & 0 deletions scripts/006-normalize-llvm-psp-archives.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
#!/usr/bin/env bash

set -euo pipefail

if [[ -z "${PSPDEV:-}" ]]; then
echo "ERROR: PSPDEV must be set before normalizing PSP archives."
exit 1
fi

ARCHIVE_DIRS=()
add_archive_dir() {
local dir="$1"
if [[ -d "${dir}" ]]; then
ARCHIVE_DIRS+=("${dir}")
fi
}

add_archive_dir "${PSPDEV}/psp/lib"
add_archive_dir "${PSPDEV}/psp/sdk/lib"
if [[ -d "${PSPDEV}/lib/gcc/psp" ]]; then
while IFS= read -r dir; do
add_archive_dir "${dir}"
done < <(find "${PSPDEV}/lib/gcc/psp" -mindepth 1 -maxdepth 1 -type d | sort)
fi

if [[ "${#ARCHIVE_DIRS[@]}" -eq 0 ]]; then
echo "No PSP archive directories found under ${PSPDEV}; skipping archive normalization."
exit 0
fi

find_tool() {
local tool="$1"
if command -v "${tool}" >/dev/null 2>&1; then
command -v "${tool}"
return 0
fi

if command -v brew >/dev/null 2>&1; then
local brew_llvm
brew_llvm="$(brew --prefix llvm 2>/dev/null || true)"
if [[ -n "${brew_llvm}" && -x "${brew_llvm}/bin/${tool}" ]]; then
echo "${brew_llvm}/bin/${tool}"
return 0
fi
fi

for prefix in /opt/homebrew/opt/llvm /usr/local/opt/llvm; do
if [[ -x "${prefix}/bin/${tool}" ]]; then
echo "${prefix}/bin/${tool}"
return 0
fi
done

echo "ERROR: ${tool} not found. Install LLVM before running this step." >&2
return 1
}

LLVM_AR="$(find_tool llvm-ar)"
LLVM_RANLIB="$(find_tool llvm-ranlib)"
LLVM_READELF="$(find_tool llvm-readelf)"
LLVM_OBJCOPY="$(find_tool llvm-objcopy)"

if ! command -v perl >/dev/null 2>&1; then
echo "ERROR: perl not found. A system Perl is required for ELF metadata normalization."
exit 1
fi

WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/pspdev-normalize-archives.XXXXXX")"
trap 'rm -rf "${WORK_DIR}"' EXIT

PATCH_ELF="${WORK_DIR}/patch-mips-psp-elf.pl"
cat > "${PATCH_ELF}" <<'PERL'
use strict;
use warnings;

for my $path (@ARGV) {
open(my $fh, "+<:raw", $path) or die "open $path: $!";
local $/;
my $data = <$fh>;
next unless length($data) >= 52;
next unless substr($data, 0, 4) eq "\x7fELF";
next unless unpack("C", substr($data, 4, 1)) == 1; # ELFCLASS32
next unless unpack("C", substr($data, 5, 1)) == 1; # ELFDATA2LSB
next unless unpack("v", substr($data, 18, 2)) == 8; # EM_MIPS

# Clang's MIPS objects can carry machine-extension and single-float metadata
# that rust-lld rejects when linked with rustc's current mipsel-sony-psp
# objects. Match the metadata shape emitted by rustc and the legacy PSP SDK:
# MIPS2 O32, no machine extension, hard-float double ABI.
my $flags = unpack("V", substr($data, 36, 4));
substr($data, 36, 4) = pack("V", $flags & 0xf000ffff);

my $shoff = unpack("V", substr($data, 32, 4));
my $shentsize = unpack("v", substr($data, 46, 2));
my $shnum = unpack("v", substr($data, 48, 2));
my $shstrndx = unpack("v", substr($data, 50, 2));
if ($shoff && $shentsize && $shstrndx < $shnum) {
my $shstr = $shoff + $shstrndx * $shentsize;
if ($shstr + 24 <= length($data)) {
my $shstr_off = unpack("V", substr($data, $shstr + 16, 4));
my $shstr_size = unpack("V", substr($data, $shstr + 20, 4));
my $shstr_end = $shstr_off + $shstr_size;
if ($shstr_end <= length($data)) {
for (my $i = 0; $i < $shnum; $i++) {
my $sh = $shoff + $i * $shentsize;
next if $sh + 24 > length($data);
my $name_off = unpack("V", substr($data, $sh, 4));
my $name_start = $shstr_off + $name_off;
next if $name_start >= $shstr_end;
my $name_end = index($data, "\0", $name_start);
next if $name_end < 0 || $name_end > $shstr_end;
my $name = substr($data, $name_start, $name_end - $name_start);

if ($name eq ".MIPS.abiflags") {
my $off = unpack("V", substr($data, $sh + 16, 4));
my $size = unpack("V", substr($data, $sh + 20, 4));
next if $size < 24 || $off + $size > length($data);
substr($data, $off + 7, 1) = "\x01"; # Val_GNU_MIPS_ABI_FP_DOUBLE
substr($data, $off + 8, 4) = pack("V", 0); # ISA extension: none
substr($data, $off + 12, 4) = pack("V", 0); # ASEs: none
substr($data, $off + 16, 4) = pack("V", 1); # FLAGS1: odd single-precision regs
}

if ($name eq ".gnu.attributes") {
my $off = unpack("V", substr($data, $sh + 16, 4));
my $size = unpack("V", substr($data, $sh + 20, 4));
next if $off + $size > length($data);

my $attrs = substr($data, $off, $size);
# GNU MIPS Tag_GNU_MIPS_ABI_FP is encoded as tag 4 followed
# by a small uleb128 value. Clang emits value 2 here while
# the SDK/rustc-compatible .MIPS.abiflags metadata says
# double-float. Keep both metadata sources in sync.
$attrs =~ s/\x04\x02/\x04\x01/g;
substr($data, $off, $size) = $attrs;
}
}
}
}
}

seek($fh, 0, 0) or die "seek $path: $!";
print {$fh} $data or die "write $path: $!";
truncate($fh, length($data)) or die "truncate $path: $!";
close($fh) or die "close $path: $!";
}
PERL

normalize_archive() {
local archive="$1"
local name
name="$(basename "${archive}")"
local archive_dir
archive_dir="$(mktemp -d "${WORK_DIR}/${name%.a}.XXXXXX")"

(
cd "${archive_dir}"
"${LLVM_AR}" x "${archive}"
)

local member_list="${WORK_DIR}/${name%.a}.members"
find "${archive_dir}" -type f | sort > "${member_list}"
if [[ ! -s "${member_list}" ]]; then
echo "Skipping empty PSP archive: ${archive#${PSPDEV}/}"
return
fi

local changed=0
while IFS= read -r obj; do
local file_symbols
file_symbols="$("${LLVM_READELF}" -s "${obj}" 2>/dev/null | awk '$4 == "FILE" && $5 == "LOCAL" && $8 != "" { print $8 }' | sort -u || true)"
if [[ -n "${file_symbols}" ]]; then
changed=1
local strip_args=()
while IFS= read -r sym; do
strip_args+=("--strip-symbol=${sym}")
done <<< "${file_symbols}"
"${LLVM_OBJCOPY}" "${strip_args[@]}" "${obj}"
fi
done < "${member_list}"

while IFS= read -r obj; do
perl "${PATCH_ELF}" "${obj}"
done < "${member_list}"

rm -f "${archive}"
local members=()
while IFS= read -r obj; do
members+=("${obj#${archive_dir}/}")
done < "${member_list}"
(
cd "${archive_dir}"
"${LLVM_AR}" rc "${archive}" "${members[@]}"
)
"${LLVM_RANLIB}" "${archive}"

if [[ "${changed}" -eq 1 ]]; then
echo "Normalized PSP archive symbols and metadata: ${archive#${PSPDEV}/}"
else
echo "Normalized PSP archive metadata: ${archive#${PSPDEV}/}"
fi
}

for dir in "${ARCHIVE_DIRS[@]}"; do
for archive in "${dir}"/*.a; do
[[ -e "${archive}" ]] || continue
normalize_archive "${archive}"
done
done
Loading