1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
module ELFShim
# See: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
MAGIC_NUMBER_OFFSET = 0
MAGIC_NUMBER_ASCII = "\x7fELF".freeze
OS_ABI_OFFSET = 0x07
OS_ABI_SYSTEM_V = 0
OS_ABI_LINUX = 3
TYPE_OFFSET = 0x10
TYPE_EXECUTABLE = 2
TYPE_SHARED = 3
ARCHITECTURE_OFFSET = 0x12
ARCHITECTURE_I386 = 0x3
ARCHITECTURE_POWERPC = 0x14
ARCHITECTURE_ARM = 0x28
ARCHITECTURE_X86_64 = 0x62
ARCHITECTURE_AARCH64 = 0xB7
def read_uint8(offset)
read(1, offset).unpack("C").first
end
def read_uint16(offset)
read(2, offset).unpack("v").first
end
def elf?
return @elf if defined? @elf
return @elf = false unless read(MAGIC_NUMBER_ASCII.size, MAGIC_NUMBER_OFFSET) == MAGIC_NUMBER_ASCII
# Check that this ELF file is for Linux or System V.
# OS_ABI is often set to 0 (System V), regardless of the target platform.
@elf = [OS_ABI_LINUX, OS_ABI_SYSTEM_V].include? read_uint8(OS_ABI_OFFSET)
end
def arch
return :dunno unless elf?
@arch ||= case read_uint16(ARCHITECTURE_OFFSET)
when ARCHITECTURE_I386 then :i386
when ARCHITECTURE_X86_64 then :x86_64
when ARCHITECTURE_POWERPC then :powerpc
when ARCHITECTURE_ARM then :arm
when ARCHITECTURE_AARCH64 then :arm64
else :dunno
end
end
def elf_type
return :dunno unless elf?
@elf_type ||= case read_uint16(TYPE_OFFSET)
when TYPE_EXECUTABLE then :executable
when TYPE_SHARED then :dylib
else :dunno
end
end
def dylib?
elf_type == :dylib
end
def binary_executable?
elf_type == :executable
end
def dynamic_elf?
return @dynamic_elf if defined? @dynamic_elf
if which "readelf"
Utils.popen_read("readelf", "-l", to_path).include?(" DYNAMIC ")
elsif which "file"
!Utils.popen_read("file", "-L", "-b", to_path)[/dynamic|shared/].nil?
else
raise "Please install either readelf (from binutils) or file."
end
end
class Metadata
attr_reader :path, :dylib_id, :dylibs
def initialize(path)
@path = path
@dylibs = []
@dylib_id, needed = needed_libraries path
return if needed.empty?
ldd = DevelopmentTools.locate "ldd"
ldd_output = Utils.popen_read(ldd, path.expand_path.to_s).split("\n")
return unless $CHILD_STATUS.success?
ldd_paths = ldd_output.map do |line|
match = line.match(/\t.+ => (.+) \(.+\)|\t(.+) => not found/)
next unless match
match.captures.compact.first
end.compact
@dylibs = ldd_paths.select do |ldd_path|
next true unless ldd_path.start_with? "/"
needed.include? File.basename(ldd_path)
end
end
private
def needed_libraries(path)
if DevelopmentTools.locate "readelf"
needed_libraries_using_readelf path
elsif DevelopmentTools.locate "patchelf"
needed_libraries_using_patchelf path
else
raise "patchelf must be installed: brew install patchelf"
end
end
def needed_libraries_using_patchelf(path)
patchelf = DevelopmentTools.locate "patchelf"
if path.dylib?
command = [patchelf, "--print-soname", path.expand_path.to_s]
soname = Utils.popen_read(*command).chomp
raise ErrorDuringExecution, command unless $CHILD_STATUS.success?
end
command = [patchelf, "--print-needed", path.expand_path.to_s]
needed = Utils.popen_read(*command).split("\n")
raise ErrorDuringExecution, command unless $CHILD_STATUS.success?
[soname, needed]
end
def needed_libraries_using_readelf(path)
soname = nil
needed = []
command = ["readelf", "-d", path.expand_path.to_s]
lines = Utils.popen_read(*command).split("\n")
raise ErrorDuringExecution, command unless $CHILD_STATUS.success?
lines.each do |s|
filename = s[/\[(.*)\]/, 1]
next if filename.nil?
if s.include? "(SONAME)"
soname = filename
elsif s.include? "(NEEDED)"
needed << filename
end
end
[soname, needed]
end
end
def metadata
@metadata ||= Metadata.new(self)
end
def dylib_id
metadata.dylib_id
end
def dynamically_linked_libraries(*)
metadata.dylibs
end
end
|