diff options
Diffstat (limited to 'meta-vitis-tc/scripts/relocate-wrapper.py')
-rwxr-xr-x | meta-vitis-tc/scripts/relocate-wrapper.py | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/meta-vitis-tc/scripts/relocate-wrapper.py b/meta-vitis-tc/scripts/relocate-wrapper.py new file mode 100755 index 00000000..d6c63edf --- /dev/null +++ b/meta-vitis-tc/scripts/relocate-wrapper.py | |||
@@ -0,0 +1,247 @@ | |||
1 | #!/usr/bin/env python3 | ||
2 | # | ||
3 | # Copyright (c) 2012 Intel Corporation | ||
4 | # | ||
5 | # SPDX-License-Identifier: GPL-2.0-only | ||
6 | # | ||
7 | # AUTHORS | ||
8 | # Laurentiu Palcu <laurentiu.palcu@intel.com> | ||
9 | # | ||
10 | |||
11 | # Copyright (C) 2019-2020, Xilinx, Inc. All rights reserved. | ||
12 | # Copyright (C) 2023, Advanced Micro Devices, Inc. All rights reserved. | ||
13 | # | ||
14 | # DESCRIPTION | ||
15 | # Runtime-relocation wrapper scripting based on YP relocation scripting | ||
16 | # | ||
17 | # AUTHORS | ||
18 | # Mark Hatle <mark.hatle@amd.com> | ||
19 | |||
20 | import struct | ||
21 | import sys | ||
22 | import stat | ||
23 | import os | ||
24 | import re | ||
25 | import errno | ||
26 | |||
27 | if sys.version < '3': | ||
28 | def b(x): | ||
29 | return x | ||
30 | else: | ||
31 | def b(x): | ||
32 | return x.encode(sys.getfilesystemencoding()) | ||
33 | |||
34 | old_prefix = re.compile(b("##DEFAULT_INSTALL_DIR##")) | ||
35 | |||
36 | def get_arch(): | ||
37 | global endian_prefix | ||
38 | f.seek(0) | ||
39 | e_ident =f.read(16) | ||
40 | ei_mag0,ei_mag1_3,ei_class,ei_data,ei_version = struct.unpack("<B3sBBB9x", e_ident) | ||
41 | |||
42 | # ei_data = 1 for little-endian & 0 for big-endian | ||
43 | if ei_data == 1: | ||
44 | endian_prefix = '<' | ||
45 | else: | ||
46 | endian_prefix = '>' | ||
47 | |||
48 | if (ei_mag0 != 0x7f and ei_mag1_3 != "ELF") or ei_class == 0: | ||
49 | return 0 | ||
50 | |||
51 | if ei_class == 1: | ||
52 | return 32 | ||
53 | elif ei_class == 2: | ||
54 | return 64 | ||
55 | |||
56 | def parse_elf_header(f): | ||
57 | global e_type, e_machine, e_version, e_entry, e_phoff, e_shoff, e_flags,\ | ||
58 | e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx | ||
59 | |||
60 | f.seek(0) | ||
61 | elf_header = f.read(64) | ||
62 | |||
63 | if arch == 32: | ||
64 | # 32bit | ||
65 | hdr_fmt = endian_prefix + "HHILLLIHHHHHH" | ||
66 | hdr_size = 52 | ||
67 | else: | ||
68 | # 64bit | ||
69 | hdr_fmt = endian_prefix + "HHIQQQIHHHHHH" | ||
70 | hdr_size = 64 | ||
71 | |||
72 | e_type, e_machine, e_version, e_entry, e_phoff, e_shoff, e_flags,\ | ||
73 | e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx =\ | ||
74 | struct.unpack(hdr_fmt, elf_header[16:hdr_size]) | ||
75 | |||
76 | def is_elf_executable(f): | ||
77 | global interp | ||
78 | |||
79 | rc = False | ||
80 | |||
81 | if arch == 32: | ||
82 | ph_fmt = endian_prefix + "IIIIIIII" | ||
83 | else: | ||
84 | ph_fmt = endian_prefix + "IIQQQQQQ" | ||
85 | |||
86 | """ look for PT_INTERP section """ | ||
87 | for i in range(0,e_phnum): | ||
88 | f.seek(e_phoff + i * e_phentsize) | ||
89 | ph_hdr = f.read(e_phentsize) | ||
90 | if arch == 32: | ||
91 | # 32bit | ||
92 | p_type, p_offset, p_vaddr, p_paddr, p_filesz,\ | ||
93 | p_memsz, p_flags, p_align = struct.unpack(ph_fmt, ph_hdr) | ||
94 | else: | ||
95 | # 64bit | ||
96 | p_type, p_flags, p_offset, p_vaddr, p_paddr, \ | ||
97 | p_filesz, p_memsz, p_align = struct.unpack(ph_fmt, ph_hdr) | ||
98 | |||
99 | """ change interpreter """ | ||
100 | if p_type == 3: | ||
101 | # PT_INTERP section | ||
102 | f.seek(p_offset) | ||
103 | # External SDKs with mixed pre-compiled binaries should not get | ||
104 | # relocated so look for some variant of /lib | ||
105 | fname = f.read(11) | ||
106 | if fname.startswith(b("/lib/")) or fname.startswith(b("/lib64/")) or \ | ||
107 | fname.startswith(b("/lib32/")) or fname.startswith(b("/usr/lib32/")) or \ | ||
108 | fname.startswith(b("/usr/lib32/")) or fname.startswith(b("/usr/lib64/")): | ||
109 | break | ||
110 | if p_filesz == 0: | ||
111 | break | ||
112 | rc = True | ||
113 | # Store the interpretor name to global interp | ||
114 | f.seek(p_offset) | ||
115 | chars = [] | ||
116 | while True: | ||
117 | c = f.read(1) | ||
118 | if c == b'\x00': | ||
119 | interp = (b''.join(chars)).decode('utf-8') | ||
120 | break | ||
121 | chars.append(c) | ||
122 | break | ||
123 | |||
124 | return rc | ||
125 | |||
126 | # MAIN | ||
127 | if len(sys.argv) < 2: | ||
128 | print('%s: <path>' % sys.argv[0]) | ||
129 | sys.exit(-1) | ||
130 | |||
131 | # In python > 3, strings may also contain Unicode characters. So, convert | ||
132 | # them to bytes | ||
133 | if sys.version_info < (3,): | ||
134 | process_path = sys.argv[1] | ||
135 | else: | ||
136 | process_path = sys.argv[1] | ||
137 | |||
138 | process_path = os.path.realpath(process_path) | ||
139 | |||
140 | for root, _, files in os.walk(process_path): | ||
141 | for file in files: | ||
142 | if file.endswith('.real'): | ||
143 | continue | ||
144 | |||
145 | e = os.path.join(root, file) | ||
146 | |||
147 | if not os.path.isfile(e) or not os.access(e, os.X_OK) or os.path.islink(e): | ||
148 | continue | ||
149 | |||
150 | if os.path.dirname(e).endswith('/lib') and (os.path.basename(e).startswith('libc-') or os.path.basename(e).startswith('libc.so')): | ||
151 | # Special case, don't wrap this... | ||
152 | continue | ||
153 | |||
154 | if os.path.dirname(e).endswith('/lib') and os.path.basename(e).startswith('libpthread-'): | ||
155 | # Special case, don't wrap this... | ||
156 | continue | ||
157 | |||
158 | perms = os.stat(e)[stat.ST_MODE] | ||
159 | if os.access(e, os.R_OK): | ||
160 | perms = None | ||
161 | else: | ||
162 | os.chmod(e, perms|stat.S_IRWXU) | ||
163 | |||
164 | try: | ||
165 | f = open(e, "r+b") | ||
166 | except IOError: | ||
167 | exctype, ioex = sys.exc_info()[:2] | ||
168 | if ioex.errno == errno.ETXTBSY: | ||
169 | print("Could not open %s. File used by another process.\nPlease "\ | ||
170 | "make sure you exit all processes that might use any SDK "\ | ||
171 | "binaries." % e) | ||
172 | else: | ||
173 | print("Could not open %s: %s(%d)" % (e, ioex.strerror, ioex.errno)) | ||
174 | sys.exit(-1) | ||
175 | |||
176 | # Save old size and do a size check at the end. Just a safety measure. | ||
177 | old_size = os.path.getsize(e) | ||
178 | if old_size >= 64: | ||
179 | arch = get_arch() | ||
180 | if arch: | ||
181 | parse_elf_header(f) | ||
182 | |||
183 | if is_elf_executable(f): | ||
184 | dirpath = os.path.dirname(e) | ||
185 | destfile = os.path.join(dirpath, file + '.real') | ||
186 | |||
187 | wrapper = os.path.join(dirpath, 'execwrapper.sh') | ||
188 | |||
189 | if not os.path.exists(wrapper): | ||
190 | #print('write %s' % wrapper) | ||
191 | with open(wrapper, "w+") as wrapperf: | ||
192 | ldso = os.path.basename(interp) | ||
193 | libbasepath = os.path.dirname(interp) # should be /lib | ||
194 | libdirname = os.path.basename(libbasepath) # lib or lib32 or lib64 or .... | ||
195 | basepath = os.path.dirname(libbasepath) # should be / | ||
196 | |||
197 | libpath = os.path.join(basepath, 'usr', libdirname) | ||
198 | |||
199 | # Generate relative names to the path of the execwrapper | ||
200 | libbasepath = os.path.relpath(libbasepath, dirpath) | ||
201 | libpath = os.path.relpath(libpath, dirpath) | ||
202 | |||
203 | print('') | ||
204 | print('wrapper: %s' % wrapper) | ||
205 | print('ldso = %s' % ldso) | ||
206 | print('lib = %s' % libdirname) | ||
207 | print('libpath = %s' % libbasepath) | ||
208 | print('usrlibpath = %s' % libpath) | ||
209 | print('') | ||
210 | |||
211 | wrapperf.write('#!/bin/bash\n') | ||
212 | wrapperf.write('# Written by Mark Hatle <mark.hatle@amd.com>\n') | ||
213 | wrapperf.write('# Copyright (C) 2019-2020, Xilinx, Inc. All rights reserved\n') | ||
214 | wrapperf.write('# Copyright (C) 2023-2024, Advanced Micro Devices, Inc. All rights reserved\n') | ||
215 | wrapperf.write('#\n') | ||
216 | wrapperf.write('# SPDX-License-Identifier: GPL-2.0-only\n') | ||
217 | wrapperf.write('LDSO=%s\n' % ldso) | ||
218 | wrapperf.write('LIBBASEPATH=%s\n' % libbasepath) | ||
219 | wrapperf.write('LIBPATH=%s\n' % libpath) | ||
220 | wrapperf.write('executable=$(basename $0)\n') | ||
221 | wrapperf.write('wrapper=$0\n') | ||
222 | wrapperf.write('BASEPATH=$(dirname ${wrapper})\n') | ||
223 | wrapperf.write('if [ ! -x $0 ]; then\n') | ||
224 | wrapperf.write(' wrapper=$(which $0)\n') | ||
225 | wrapperf.write('fi\n') | ||
226 | wrapperf.write('if [ -h $0 ]; then\n') | ||
227 | wrapperf.write(' executable=$(basename "$(readlink $0)" )\n') | ||
228 | wrapperf.write(' BASEPATH=$(dirname "$(realpath $0)")\n') | ||
229 | wrapperf.write('fi\n') | ||
230 | wrapperf.write('LIBBASEPATH=$(realpath ${BASEPATH}/${LIBBASEPATH})\n') | ||
231 | wrapperf.write('LIBPATH=$(realpath ${BASEPATH}/${LIBPATH})\n') | ||
232 | wrapperf.write('export COLLECT_GCC=${COLLECT_GCC%%.real}\n') | ||
233 | wrapperf.write('exec ${LIBBASEPATH}/${LDSO} --library-path ${LIBPATH}:${LIBBASEPATH} ${BASEPATH}/${executable}.real $@\n') | ||
234 | #print('chmod %s 0775' % wrapper) | ||
235 | os.chmod(wrapper, 0o775) | ||
236 | |||
237 | print('%s -> %s' % (e, destfile)) | ||
238 | #print('mv %s %s' % (e, destfile)) | ||
239 | os.rename(e, destfile) | ||
240 | #print('ln %s %s' % (wrapper, e)) | ||
241 | os.link(wrapper, e) | ||
242 | |||
243 | """ change permissions back """ | ||
244 | if perms: | ||
245 | os.chmod(e, perms) | ||
246 | |||
247 | f.close() | ||