elf: expand $ORIGIN in RUNPATH/RPATH entries

This commit is contained in:
Caleb Xu 2024-04-24 23:56:55 -04:00
parent 4bc1cc57f3
commit 6ee34832d1
No known key found for this signature in database
GPG Key ID: 47E6040D07B8407D
2 changed files with 122 additions and 0 deletions

View File

@ -153,6 +153,7 @@ module ELFShim
# Search for dependencies in the runpath/rpath first
local_paths&.each do |local_path|
local_path = OS::Linux::Elf.expand_elf_dst(local_path, "ORIGIN", path.parent)
candidate = Pathname(local_path)/basename
return candidate if candidate.exist? && candidate.elf?
end
@ -225,3 +226,37 @@ module ELFShim
metadata.dylibs
end
end
module OS
module Linux
# Helper functions for working with ELF objects.
#
# @api private
module Elf
sig { params(str: String, ref: String, repl: T.any(String, Pathname)).returns(String) }
def self.expand_elf_dst(str, ref, repl)
# ELF gABI rules for DSTs:
# - Longest possible sequence using the rules (greedy).
# - Must start with a $ (enforced by caller).
# - Must follow $ with one underscore or ASCII [A-Za-z] (caller
# follows these rules for REF) or '{' (start curly quoted name).
# - Must follow first two characters with zero or more [A-Za-z0-9_]
# (enforced by caller) or '}' (end curly quoted name).
# (from https://github.com/bminor/glibc/blob/41903cb6f460d62ba6dd2f4883116e2a624ee6f8/elf/dl-load.c#L182-L228)
# In addition to capturing a token, also attempt to capture opening/closing braces and check that they are not
# mismatched before expanding.
str.gsub(/\$({?)([a-zA-Z_][a-zA-Z0-9_]*)(}?)/) do |orig_str|
has_opening_brace = ::Regexp.last_match(1).present?
matched_text = ::Regexp.last_match(2)
has_closing_brace = ::Regexp.last_match(3).present?
if (matched_text == ref) && (has_opening_brace == has_closing_brace)
repl
else
orig_str
end
end
end
end
end
end

View File

@ -0,0 +1,87 @@
# frozen_string_literal: true
RSpec.describe OS::Linux::Elf do
describe "::expand_elf_dst" do
it "expands tokens that are not wrapped in curly braces" do
str = "$ORIGIN/../lib"
ref = "ORIGIN"
repl = "/opt/homebrew/bin"
expected = "/opt/homebrew/bin/../lib"
expect(described_class.expand_elf_dst(str, ref, repl)).to eq(expected)
end
it "expands tokens that are wrapped in curly braces" do
str = "${ORIGIN}/../lib"
ref = "ORIGIN"
repl = "/opt/homebrew/bin"
expected = "/opt/homebrew/bin/../lib"
expect(described_class.expand_elf_dst(str, ref, repl)).to eq(expected)
str = "${ORIGIN}new/../lib"
ref = "ORIGIN"
repl = "/opt/homebrew/bin"
expected = "/opt/homebrew/binnew/../lib"
expect(described_class.expand_elf_dst(str, ref, repl)).to eq(expected)
end
it "expands multiple occurrences of token" do
str = "${ORIGIN}/../..$ORIGIN/../lib"
ref = "ORIGIN"
repl = "/opt/homebrew/bin"
expected = "/opt/homebrew/bin/../../opt/homebrew/bin/../lib"
expect(described_class.expand_elf_dst(str, ref, repl)).to eq(expected)
end
it "rejects and passes through tokens containing additional characters" do
str = "$ORIGINAL/../lib"
ref = "ORIGIN"
repl = "/opt/homebrew/bin"
expected = "$ORIGINAL/../lib"
expect(described_class.expand_elf_dst(str, ref, repl)).to eq(expected)
str = "$ORIGIN_/../lib"
ref = "ORIGIN"
repl = "/opt/homebrew/bin"
expected = "$ORIGIN_/../lib"
expect(described_class.expand_elf_dst(str, ref, repl)).to eq(expected)
str = "$ORIGIN_STORY/../lib"
ref = "ORIGIN"
repl = "/opt/homebrew/bin"
expected = "$ORIGIN_STORY/../lib"
expect(described_class.expand_elf_dst(str, ref, repl)).to eq(expected)
str = "${ORIGINAL}/../lib"
ref = "ORIGIN"
repl = "/opt/homebrew/bin"
expected = "${ORIGINAL}/../lib"
expect(described_class.expand_elf_dst(str, ref, repl)).to eq(expected)
str = "${ORIGIN_}/../lib"
ref = "ORIGIN"
repl = "/opt/homebrew/bin"
expected = "${ORIGIN_}/../lib"
expect(described_class.expand_elf_dst(str, ref, repl)).to eq(expected)
str = "${ORIGIN_STORY}/../lib"
ref = "ORIGIN"
repl = "/opt/homebrew/bin"
expected = "${ORIGIN_STORY}/../lib"
expect(described_class.expand_elf_dst(str, ref, repl)).to eq(expected)
end
it "rejects and passes through tokens with mismatched curly braces" do
str = "${ORIGIN/../lib"
ref = "ORIGIN"
repl = "/opt/homebrew/bin"
expected = "${ORIGIN/../lib"
expect(described_class.expand_elf_dst(str, ref, repl)).to eq(expected)
str = "$ORIGIN}/../lib"
ref = "ORIGIN"
repl = "/opt/homebrew/bin"
expected = "$ORIGIN}/../lib"
expect(described_class.expand_elf_dst(str, ref, repl)).to eq(expected)
end
end
end