1#!/usr/bin/env python3
2
3import re
4import tempfile
5
6from vndk_definition_tool import (
7    ELF, ELFLinker, GenericRefs, PT_SYSTEM, PT_VENDOR, VNDKLibDir)
8
9from .compat import StringIO, TestCase, patch
10from .utils import GraphBuilder
11
12
13class ELFLinkerTest(TestCase):
14    def _create_normal_graph(self):
15        gb = GraphBuilder()
16
17        gb.add_multilib(PT_SYSTEM, 'libdl',
18                        exported_symbols={'dlclose', 'dlopen', 'dlsym'})
19
20        gb.add_multilib(PT_SYSTEM, 'libm', exported_symbols={'cos', 'sin'})
21
22        gb.add_multilib(PT_SYSTEM, 'libc', dt_needed=['libdl.so', 'libm.so'],
23                        exported_symbols={'fclose', 'fopen', 'fread'},
24                        imported_symbols={'dlclose', 'dlopen', 'cos', 'sin'})
25
26        gb.add_multilib(PT_SYSTEM, 'libRS', dt_needed=['libdl.so'],
27                        exported_symbols={'rsContextCreate'},
28                        imported_symbols={'dlclose', 'dlopen', 'dlsym'})
29
30        gb.add_multilib(PT_SYSTEM, 'libcutils',
31                        dt_needed=['libc.so', 'libdl.so'],
32                        imported_symbols={'dlclose', 'dlopen', 'fclose',
33                                          'fopen'})
34
35        gb.add_multilib(PT_VENDOR, 'libEGL',
36                        dt_needed=['libc.so', 'libcutils.so', 'libdl.so'],
37                        exported_symbols={'eglGetDisplay'},
38                        imported_symbols={'fclose', 'fopen'})
39
40        gb.resolve()
41        return gb
42
43
44    def _get_paths_from_nodes(self, nodes):
45        return sorted([node.path for node in nodes])
46
47
48    def test_get_lib(self):
49        gb = self._create_normal_graph()
50        graph = gb.graph
51
52        node = graph.get_lib('/system/lib/libc.so')
53        self.assertEqual(gb.libc_32, node)
54        self.assertEqual('/system/lib/libc.so', node.path)
55
56        node = graph.get_lib('/system/lib64/libdl.so')
57        self.assertEqual(gb.libdl_64, node)
58        self.assertEqual('/system/lib64/libdl.so', node.path)
59
60        node = graph.get_lib('/vendor/lib64/libEGL.so')
61        self.assertEqual(gb.libEGL_64, node)
62        self.assertEqual('/vendor/lib64/libEGL.so', node.path)
63
64        self.assertEqual(None, graph.get_lib('/no/such/path.so'))
65
66
67    def test_map_paths_to_libs(self):
68        gb = self._create_normal_graph()
69        graph = gb.graph
70
71        bad = []
72        paths = ['/system/lib/libc.so', '/system/lib/libdl.so']
73        nodes = graph.get_libs(paths, bad.append)
74
75        self.assertEqual([], bad)
76        self.assertEqual(2, len(nodes))
77        self.assertEqual(paths, self._get_paths_from_nodes(nodes))
78
79        bad = []
80        paths = ['/no/such/path.so', '/system/lib64/libdl.so']
81        nodes = graph.get_libs(paths, bad.append)
82        self.assertEqual(['/no/such/path.so'], bad)
83        self.assertEqual(['/system/lib64/libdl.so'],
84                         self._get_paths_from_nodes(nodes))
85
86
87    def test_elf_class_and_partitions(self):
88        gb = self._create_normal_graph()
89        graph = gb.graph
90        self.assertEqual(5, len(graph.lib_pt[PT_SYSTEM].lib32))
91        self.assertEqual(5, len(graph.lib_pt[PT_SYSTEM].lib64))
92        self.assertEqual(1, len(graph.lib_pt[PT_VENDOR].lib32))
93        self.assertEqual(1, len(graph.lib_pt[PT_VENDOR].lib64))
94
95
96    def test_deps(self):
97        gb = self._create_normal_graph()
98
99        # Check the dependencies of libc.so.
100        node = gb.graph.get_lib('/system/lib/libc.so')
101        self.assertEqual(['/system/lib/libdl.so', '/system/lib/libm.so'],
102                         self._get_paths_from_nodes(node.deps_all))
103
104        # Check the dependencies of libRS.so.
105        node = gb.graph.get_lib('/system/lib64/libRS.so')
106        self.assertEqual(['/system/lib64/libdl.so'],
107                         self._get_paths_from_nodes(node.deps_all))
108
109        # Check the dependencies of libEGL.so.
110        node = gb.graph.get_lib('/vendor/lib64/libEGL.so')
111        self.assertEqual(['/system/lib64/libc.so', '/system/lib64/libcutils.so',
112                          '/system/lib64/libdl.so'],
113                         self._get_paths_from_nodes(node.deps_all))
114
115
116    def test_linked_symbols(self):
117        gb = self._create_normal_graph()
118        graph = gb.graph
119
120        # Check the unresolved symbols.
121        for lib_set in graph.lib_pt:
122            for lib in lib_set.values():
123                self.assertEqual(set(), lib.unresolved_symbols)
124
125        # Check the linked symbols.
126        for lib in ('lib', 'lib64'):
127            libdl = graph.get_lib('/system/' + lib + '/libdl.so')
128            libm = graph.get_lib('/system/' + lib + '/libm.so')
129            libc = graph.get_lib('/system/' + lib + '/libc.so')
130            libRS = graph.get_lib('/system/' + lib + '/libRS.so')
131            libcutils = \
132                    graph.get_lib('/system/' + lib + '/libcutils.so')
133            libEGL = graph.get_lib('/vendor/' + lib + '/libEGL.so')
134
135            # Check the linked symbols for libc.so.
136            self.assertIs(libdl, libc.linked_symbols['dlclose'])
137            self.assertIs(libdl, libc.linked_symbols['dlopen'])
138            self.assertIs(libm, libc.linked_symbols['cos'])
139            self.assertIs(libm, libc.linked_symbols['sin'])
140
141            # Check the linked symbols for libRS.so.
142            self.assertIs(libdl, libRS.linked_symbols['dlclose'])
143            self.assertIs(libdl, libRS.linked_symbols['dlopen'])
144            self.assertIs(libdl, libRS.linked_symbols['dlsym'])
145
146            # Check the linked symbols for libcutils.so.
147            self.assertIs(libdl, libcutils.linked_symbols['dlclose'])
148            self.assertIs(libdl, libcutils.linked_symbols['dlopen'])
149            self.assertIs(libc, libcutils.linked_symbols['fclose'])
150            self.assertIs(libc, libcutils.linked_symbols['fopen'])
151
152            # Check the linked symbols for libEGL.so.
153            self.assertIs(libc, libEGL.linked_symbols['fclose'])
154            self.assertIs(libc, libEGL.linked_symbols['fopen'])
155
156
157    def test_unresolved_symbols(self):
158        gb = GraphBuilder()
159        gb.add_lib(PT_SYSTEM, ELF.ELFCLASS64, 'libfoo', dt_needed=[],
160                   exported_symbols={'foo', 'bar'},
161                   imported_symbols={'__does_not_exist'})
162        gb.resolve()
163
164        lib = gb.graph.get_lib('/system/lib64/libfoo.so')
165        self.assertEqual({'__does_not_exist'}, lib.unresolved_symbols)
166
167
168    def test_users(self):
169        gb = self._create_normal_graph()
170        graph = gb.graph
171
172        # Check the users of libc.so.
173        node = graph.get_lib('/system/lib/libc.so')
174        self.assertEqual(['/system/lib/libcutils.so', '/vendor/lib/libEGL.so'],
175                         self._get_paths_from_nodes(node.users_all))
176
177        # Check the users of libdl.so.
178        node = graph.get_lib('/system/lib/libdl.so')
179        self.assertEqual(['/system/lib/libRS.so', '/system/lib/libc.so',
180                          '/system/lib/libcutils.so', '/vendor/lib/libEGL.so'],
181                         self._get_paths_from_nodes(node.users_all))
182
183        # Check the users of libRS.so.
184        node = graph.get_lib('/system/lib64/libRS.so')
185        self.assertEqual([], self._get_paths_from_nodes(node.users_all))
186
187        # Check the users of libEGL.so.
188        node = graph.get_lib('/vendor/lib64/libEGL.so')
189        self.assertEqual([], self._get_paths_from_nodes(node.users_all))
190
191
192    def test_compute_predefined_sp_hal(self):
193        gb = GraphBuilder()
194
195        # HIDL SP-HAL implementation.
196        gb.add_multilib(PT_SYSTEM, 'gralloc.default', extra_dir='hw')
197        gb.add_multilib(PT_SYSTEM, 'gralloc.chipset', extra_dir='hw')
198        gb.add_multilib(PT_SYSTEM, '[email protected]',
199                        extra_dir='hw')
200
201        # NDK loader libraries should not be considered as SP-HALs.
202        gb.add_multilib(PT_SYSTEM, 'libvulkan')
203        gb.add_multilib(PT_SYSTEM, 'libEGL')
204        gb.add_multilib(PT_SYSTEM, 'libGLESv1_CM')
205        gb.add_multilib(PT_SYSTEM, 'libGLESv2')
206        gb.add_multilib(PT_SYSTEM, 'libGLESv3')
207
208        # OpenGL implementation.
209        gb.add_multilib(PT_VENDOR, 'libEGL_chipset', extra_dir='egl')
210        gb.add_multilib(PT_VENDOR, 'libGLES_chipset', extra_dir='egl')
211        gb.add_multilib(PT_VENDOR, 'libGLESv1_CM_chipset', extra_dir='egl')
212        gb.add_multilib(PT_VENDOR, 'libGLESv2_chipset', extra_dir='egl')
213        gb.add_multilib(PT_VENDOR, 'libGLESv3_chipset', extra_dir='egl')
214
215        # Renderscript implementation.
216        gb.add_multilib(PT_VENDOR, 'libRSDriver_chipset')
217        gb.add_multilib(PT_VENDOR, 'libPVRRS')
218
219        # Vulkan implementation.
220        gb.add_multilib(PT_VENDOR, 'vulkan.chipset', extra_dir='hw')
221
222        # Some un-related libraries.
223        gb.add_multilib(PT_SYSTEM, 'libfoo')
224        gb.add_multilib(PT_VENDOR, 'libfoo')
225
226        gb.resolve()
227
228        # Compute SP-HAL.
229        sp_hals = set(lib.path for lib in gb.graph.compute_predefined_sp_hal())
230
231        for lib in ('lib', 'lib64'):
232            # Check HIDL SP-HAL implementation.
233            self.assertIn('/system/' + lib + '/hw/gralloc.default.so', sp_hals)
234            self.assertIn('/system/' + lib + '/hw/gralloc.chipset.so', sp_hals)
235            self.assertIn('/system/' + lib + '/hw/'
236                          '[email protected]',
237                          sp_hals)
238
239
240            # Check that NDK loaders are not SP-HALs.
241            self.assertNotIn('/system/' + lib + '/libvulkan.so', sp_hals)
242            self.assertNotIn('/system/' + lib + '/libEGL.so', sp_hals)
243            self.assertNotIn('/system/' + lib + '/libGLESv1_CM.so', sp_hals)
244            self.assertNotIn('/system/' + lib + '/libGLESv2.so', sp_hals)
245            self.assertNotIn('/system/' + lib + '/libGLESv3.so', sp_hals)
246
247            # Check that OpenGL implementations are SP-HALs.
248            self.assertIn('/vendor/' + lib + '/egl/libEGL_chipset.so', sp_hals)
249            self.assertIn('/vendor/' + lib + '/egl/libGLES_chipset.so',
250                          sp_hals)
251            self.assertIn('/vendor/' + lib + '/egl/libGLESv1_CM_chipset.so',
252                          sp_hals)
253            self.assertIn('/vendor/' + lib + '/egl/libGLESv2_chipset.so',
254                          sp_hals)
255            self.assertIn('/vendor/' + lib + '/egl/libGLESv3_chipset.so',
256                          sp_hals)
257
258            # Check that Renderscript implementations are SP-HALs.
259            self.assertIn('/vendor/' + lib + '/libRSDriver_chipset.so', sp_hals)
260            self.assertIn('/vendor/' + lib + '/libPVRRS.so', sp_hals)
261
262            # Check that vulkan implementation are SP-HALs.
263            self.assertIn('/vendor/' + lib + '/libPVRRS.so', sp_hals)
264
265            # Check that un-related libraries are not SP-HALs.
266            self.assertNotIn('/system/' + lib + '/libfoo.so', sp_hals)
267            self.assertNotIn('/vendor/' + lib + '/libfoo.so', sp_hals)
268
269
270    def test_compute_sp_lib(self):
271        # Create graph.
272        gb = GraphBuilder()
273
274        # LL-NDK (should be excluded from result)
275        gb.add_multilib(PT_SYSTEM, 'libc')
276
277        libEGL_32, libEGL_64 = \
278                gb.add_multilib(PT_SYSTEM, 'libEGL',
279                                dt_needed=['libc.so', 'libutils.so'])
280
281        # LL-NDK dependencies
282        gb.add_multilib(PT_SYSTEM, 'libutils',
283                        dt_needed=['libc.so', 'libcutils.so'])
284
285        # VNDK-SP used by both LL-NDK and SP-HAL
286        gb.add_multilib(PT_SYSTEM, 'libsp_both_vs')
287
288        # VNDK-SP used by LL-NDK
289        gb.add_multilib(PT_SYSTEM, 'libcutils_dep', dt_needed=['libc.so'])
290        gb.add_multilib(PT_SYSTEM, 'libcutils',
291                        dt_needed=['libc.so', 'libcutils_dep.so',
292                                   'libsp_both_vs.so'])
293
294        # VNDK-SP used by SP-HAL
295        gb.add_multilib(PT_SYSTEM, 'libhidlbase')
296        gb.add_multilib(PT_SYSTEM, 'libhidlmemory',
297                        dt_needed=['libhidlbase.so', 'libsp_both_vs.so'])
298
299        # SP-HAL dependencies
300        gb.add_multilib(PT_VENDOR, 'libllvm_vendor_dep')
301        gb.add_multilib(PT_VENDOR, 'libllvm_vendor',
302                        dt_needed=['libc.so', 'libllvm_vendor_dep.so'])
303
304        # SP-HAL
305        libEGL_chipset_32, libEGL_chipset_64 = \
306                gb.add_multilib(PT_VENDOR, 'libEGL_chipset', extra_dir='egl',
307                                dt_needed=['libc.so', 'libllvm_vendor.so',
308                                           'libhidlmemory.so'])
309
310        gb.resolve()
311
312        # Add dlopen() dependencies from libEGL to libEGL_chipset.
313        libEGL_32.add_dlopen_dep(libEGL_chipset_32)
314        libEGL_64.add_dlopen_dep(libEGL_chipset_64)
315
316        # Create generic reference.
317        class MockGenericRefs(object):
318            # pylint: disable=too-few-public-methods
319            def classify_lib(self, lib):
320                if 'libllvm_vendor' in lib.path:
321                    return GenericRefs.NEW_LIB
322                return GenericRefs.EXPORT_EQUAL
323
324        sp_lib = gb.graph.compute_sp_lib(MockGenericRefs())
325
326        self.assertEqual(2 * 1, len(sp_lib.sp_hal))
327        self.assertEqual(2 * 2, len(sp_lib.sp_hal_dep))
328        self.assertEqual(2 * 2, len(sp_lib.vndk_sp_hal))
329        self.assertEqual(2 * 2, len(sp_lib.ll_ndk))
330        self.assertEqual(2 * 3, len(sp_lib.ll_ndk_private))
331        self.assertEqual(2 * 1, len(sp_lib.vndk_sp_both))
332
333        sp_hal = self._get_paths_from_nodes(sp_lib.sp_hal)
334        sp_hal_dep = self._get_paths_from_nodes(sp_lib.sp_hal_dep)
335        vndk_sp_hal = self._get_paths_from_nodes(sp_lib.vndk_sp_hal)
336
337        ll_ndk = self._get_paths_from_nodes(sp_lib.ll_ndk)
338        ll_ndk_private = self._get_paths_from_nodes(sp_lib.ll_ndk_private)
339
340        vndk_sp_both = self._get_paths_from_nodes(sp_lib.vndk_sp_both)
341
342        for lib_dir in ('lib', 'lib64'):
343            # VNDK-SP used by both LL-NDK and SP-HAL
344            self.assertIn('/system/{}/libsp_both_vs.so'.format(lib_dir),
345                          vndk_sp_both)
346
347            # VNDK-SP used by LL-NDK
348            self.assertIn('/system/{}/libcutils.so'.format(lib_dir),
349                          ll_ndk_private)
350            self.assertIn('/system/{}/libcutils_dep.so'.format(lib_dir),
351                          ll_ndk_private)
352            self.assertIn('/system/{}/libutils.so'.format(lib_dir),
353                          ll_ndk_private)
354
355            # VNDK-SP used by SP-HAL
356            self.assertIn('/system/{}/libhidlbase.so'.format(lib_dir),
357                          vndk_sp_hal)
358            self.assertIn('/system/{}/libhidlmemory.so'.format(lib_dir),
359                          vndk_sp_hal)
360
361            # SP-HAL dependencies
362            self.assertIn('/vendor/{}/libllvm_vendor.so'.format(lib_dir),
363                          sp_hal_dep)
364            self.assertIn('/vendor/{}/libllvm_vendor_dep.so'.format(lib_dir),
365                          sp_hal_dep)
366
367            # SP-HAL
368            self.assertIn('/vendor/{}/egl/libEGL_chipset.so'.format(lib_dir),
369                          sp_hal)
370
371            # LL-NDK
372            self.assertIn('/system/{}/libEGL.so'.format(lib_dir), ll_ndk)
373            self.assertIn('/system/{}/libc.so'.format(lib_dir), ll_ndk)
374
375            # LL-NDK must not in sp_hal, sp_hal_dep, and vndk_sp_hal.
376            libc_path = '/system/{}/libc.so'.format(lib_dir)
377            self.assertNotIn(libc_path, sp_hal)
378            self.assertNotIn(libc_path, sp_hal_dep)
379            self.assertNotIn(libc_path, vndk_sp_hal)
380            self.assertNotIn(libc_path, ll_ndk_private)
381
382
383    def test_link_vndk_ver_dirs(self):
384        gb = GraphBuilder()
385
386        libc_32, libc_64 = gb.add_multilib(PT_SYSTEM, 'libc')
387
388        libvndk_a_32, libvndk_a_64 = gb.add_multilib(
389            PT_SYSTEM, 'libvndk_a', extra_dir='vndk-28',
390            dt_needed=['libc.so', 'libvndk_b.so', 'libvndk_sp_b.so'])
391
392        libvndk_b_32, libvndk_b_64 = gb.add_multilib(
393            PT_SYSTEM, 'libvndk_b', extra_dir='vndk-28',
394            dt_needed=['libc.so', 'libvndk_sp_b.so'])
395
396        libvndk_c_32, libvndk_c_64 = gb.add_multilib(
397            PT_VENDOR, 'libvndk_c', extra_dir='vndk-28',
398            dt_needed=['libc.so', 'libvndk_d.so', 'libvndk_sp_d.so'])
399
400        libvndk_d_32, libvndk_d_64 = gb.add_multilib(
401            PT_VENDOR, 'libvndk_d', extra_dir='vndk-28',
402            dt_needed=['libc.so', 'libvndk_sp_d.so'])
403
404        libvndk_sp_a_32, libvndk_sp_a_64 = gb.add_multilib(
405            PT_SYSTEM, 'libvndk_sp_a', extra_dir='vndk-sp-28',
406            dt_needed=['libc.so', 'libvndk_sp_b.so'])
407
408        libvndk_sp_b_32, libvndk_sp_b_64 = gb.add_multilib(
409            PT_SYSTEM, 'libvndk_sp_b', extra_dir='vndk-sp-28',
410            dt_needed=['libc.so'])
411
412        libvndk_sp_c_32, libvndk_sp_c_64 = gb.add_multilib(
413            PT_VENDOR, 'libvndk_sp_c', extra_dir='vndk-sp-28',
414            dt_needed=['libc.so', 'libvndk_sp_d.so'])
415
416        libvndk_sp_d_32, libvndk_sp_d_64 = gb.add_multilib(
417            PT_VENDOR, 'libvndk_sp_d', extra_dir='vndk-sp-28',
418            dt_needed=['libc.so'])
419
420        gb.resolve(VNDKLibDir.create_from_version('28'), '28')
421
422        # 32-bit shared libraries
423        self.assertIn(libc_32, libvndk_a_32.deps_all)
424        self.assertIn(libc_32, libvndk_b_32.deps_all)
425        self.assertIn(libc_32, libvndk_c_32.deps_all)
426        self.assertIn(libc_32, libvndk_d_32.deps_all)
427        self.assertIn(libc_32, libvndk_sp_a_32.deps_all)
428        self.assertIn(libc_32, libvndk_sp_b_32.deps_all)
429        self.assertIn(libc_32, libvndk_sp_c_32.deps_all)
430        self.assertIn(libc_32, libvndk_sp_d_32.deps_all)
431
432        self.assertIn(libvndk_b_32, libvndk_a_32.deps_all)
433        self.assertIn(libvndk_sp_b_32, libvndk_a_32.deps_all)
434        self.assertIn(libvndk_sp_b_32, libvndk_b_32.deps_all)
435        self.assertIn(libvndk_sp_b_32, libvndk_sp_a_32.deps_all)
436
437        self.assertIn(libvndk_d_32, libvndk_c_32.deps_all)
438        self.assertIn(libvndk_sp_d_32, libvndk_c_32.deps_all)
439        self.assertIn(libvndk_sp_d_32, libvndk_d_32.deps_all)
440        self.assertIn(libvndk_sp_d_32, libvndk_sp_c_32.deps_all)
441
442        # 64-bit shared libraries
443        self.assertIn(libc_64, libvndk_a_64.deps_all)
444        self.assertIn(libc_64, libvndk_b_64.deps_all)
445        self.assertIn(libc_64, libvndk_c_64.deps_all)
446        self.assertIn(libc_64, libvndk_d_64.deps_all)
447        self.assertIn(libc_64, libvndk_sp_a_64.deps_all)
448        self.assertIn(libc_64, libvndk_sp_b_64.deps_all)
449        self.assertIn(libc_64, libvndk_sp_c_64.deps_all)
450        self.assertIn(libc_64, libvndk_sp_d_64.deps_all)
451
452        self.assertIn(libvndk_b_64, libvndk_a_64.deps_all)
453        self.assertIn(libvndk_sp_b_64, libvndk_a_64.deps_all)
454        self.assertIn(libvndk_sp_b_64, libvndk_b_64.deps_all)
455        self.assertIn(libvndk_sp_b_64, libvndk_sp_a_64.deps_all)
456
457        self.assertIn(libvndk_d_64, libvndk_c_64.deps_all)
458        self.assertIn(libvndk_sp_d_64, libvndk_c_64.deps_all)
459        self.assertIn(libvndk_sp_d_64, libvndk_d_64.deps_all)
460        self.assertIn(libvndk_sp_d_64, libvndk_sp_c_64.deps_all)
461
462
463    def test_rewrite_apex_modules(self):
464        graph = ELFLinker()
465
466        libfoo = graph.add_lib(PT_SYSTEM, '/system/apex/foo/lib/libfoo.so',
467                               ELF(ELF.ELFCLASS32, ELF.ELFDATA2LSB))
468        libbar = graph.add_lib(PT_SYSTEM, '/system/apex/bar/lib/libbar.so',
469                               ELF(ELF.ELFCLASS32, ELF.ELFDATA2LSB))
470
471        graph.rewrite_apex_modules()
472
473        self.assertEqual(libfoo.path, '/apex/foo/lib/libfoo.so')
474        self.assertEqual(libbar.path, '/apex/bar/lib/libbar.so')
475
476
477    def test_link_apex_modules(self):
478        graph = ELFLinker()
479
480        libfoo = graph.add_lib(PT_SYSTEM, '/system/apex/foo/lib/libfoo.so',
481                               ELF(ELF.ELFCLASS32, ELF.ELFDATA2LSB))
482        libbar = graph.add_lib(PT_SYSTEM, '/system/lib/libbar.so',
483                               ELF(ELF.ELFCLASS32, ELF.ELFDATA2LSB,
484                                   dt_needed=['libfoo.so']))
485
486        graph.rewrite_apex_modules()
487        graph.resolve_deps()
488
489        self.assertIn(libfoo, libbar.deps_all)
490
491
492    def test_link_apex_bionic(self):
493        graph = ELFLinker()
494
495        libc = graph.add_lib(
496            PT_SYSTEM, '/system/apex/com.android.runtime/lib/bionic/libc.so',
497            ELF(ELF.ELFCLASS32, ELF.ELFDATA2LSB))
498        libbar = graph.add_lib(
499            PT_SYSTEM, '/system/lib/libbar.so',
500            ELF(ELF.ELFCLASS32, ELF.ELFDATA2LSB, dt_needed=['libc.so']))
501
502        graph.rewrite_apex_modules()
503        graph.resolve_deps()
504
505        self.assertIn(libc, libbar.deps_all)
506
507
508class ELFLinkerDlopenDepsTest(TestCase):
509    def test_add_dlopen_deps(self):
510        gb = GraphBuilder()
511        liba = gb.add_lib32(PT_SYSTEM, 'liba')
512        libb = gb.add_lib32(PT_SYSTEM, 'libb')
513        gb.resolve()
514
515        with tempfile.NamedTemporaryFile(mode='w') as tmp_file:
516            tmp_file.write('/system/lib/liba.so: /system/lib/libb.so')
517            tmp_file.seek(0)
518            gb.graph.add_dlopen_deps(tmp_file.name)
519
520        self.assertIn(libb, liba.deps_dlopen)
521        self.assertIn(liba, libb.users_dlopen)
522
523        self.assertNotIn(libb, liba.deps_needed)
524        self.assertNotIn(liba, libb.users_needed)
525
526
527    def test_add_dlopen_deps_lib_subst(self):
528        gb = GraphBuilder()
529        liba_32, liba_64 = gb.add_multilib(PT_SYSTEM, 'liba')
530        libb_32, libb_64 = gb.add_multilib(PT_SYSTEM, 'libb')
531        gb.resolve()
532
533        with tempfile.NamedTemporaryFile(mode='w') as tmp_file:
534            tmp_file.write('/system/${LIB}/liba.so: /system/${LIB}/libb.so')
535            tmp_file.seek(0)
536            gb.graph.add_dlopen_deps(tmp_file.name)
537
538        self.assertIn(libb_32, liba_32.deps_dlopen)
539        self.assertIn(liba_32, libb_32.users_dlopen)
540
541        self.assertIn(libb_64, liba_64.deps_dlopen)
542        self.assertIn(liba_64, libb_64.users_dlopen)
543
544
545    def test_add_dlopen_deps_lib_subset_single_bitness(self):
546        gb = GraphBuilder()
547        liba_32, liba_64 = gb.add_multilib(PT_SYSTEM, 'liba')
548        libb_32 = gb.add_lib32(PT_SYSTEM, 'libb')
549        gb.resolve()
550
551        with tempfile.NamedTemporaryFile(mode='w') as tmp_file:
552            tmp_file.write('/system/${LIB}/libb.so: /system/${LIB}/liba.so')
553            tmp_file.seek(0)
554
555            stderr = StringIO()
556            with patch('sys.stderr', stderr):
557                gb.graph.add_dlopen_deps(tmp_file.name)
558
559            self.assertEqual('', stderr.getvalue())
560
561        self.assertIn(liba_32, libb_32.deps_dlopen)
562        self.assertIn(libb_32, liba_32.users_dlopen)
563
564        self.assertEqual(0, len(liba_64.users_dlopen))
565
566
567    def test_add_dlopen_deps_regex(self):
568        gb = GraphBuilder()
569        liba = gb.add_lib32(PT_SYSTEM, 'liba')
570        libb = gb.add_lib32(PT_SYSTEM, 'libb')
571        gb.resolve()
572
573        with tempfile.NamedTemporaryFile(mode='w') as tmp_file:
574            tmp_file.write('[regex].*libb\\.so: [regex].*/${LIB}/liba\\.so')
575            tmp_file.seek(0)
576
577            stderr = StringIO()
578            with patch('sys.stderr', stderr):
579                gb.graph.add_dlopen_deps(tmp_file.name)
580
581            self.assertEqual('', stderr.getvalue())
582
583        self.assertIn(liba, libb.deps_dlopen)
584        self.assertIn(libb, liba.users_dlopen)
585
586
587    def test_add_dlopen_deps_error(self):
588        gb = GraphBuilder()
589        gb.add_lib32(PT_SYSTEM, 'liba')
590        gb.add_lib32(PT_SYSTEM, 'libb')
591        gb.resolve()
592
593        with tempfile.NamedTemporaryFile(mode='w') as tmp_file:
594            tmp_file.write('/system/lib/libc.so: /system/lib/libd.so')
595            tmp_file.seek(0)
596
597            stderr = StringIO()
598            with patch('sys.stderr', stderr):
599                gb.graph.add_dlopen_deps(tmp_file.name)
600
601            self.assertRegex(
602                stderr.getvalue(),
603                'error:' + re.escape(tmp_file.name) + ':1: ' +
604                'Failed to add dlopen dependency from .* to .*\\.\n')
605