Merge "Remove redundant dependencies"
diff --git a/vndk/tools/definition-tool/tests/test_elf_linker.py b/vndk/tools/definition-tool/tests/test_elf_linker.py
index dd7799d..1426422 100755
--- a/vndk/tools/definition-tool/tests/test_elf_linker.py
+++ b/vndk/tools/definition-tool/tests/test_elf_linker.py
@@ -269,10 +269,6 @@
             'libnativeloader',
             'libvintf',
 
-            # SP-NDK VNDK-stable (UI-related)
-            'libnativewindow',
-            'libsync',
-
             # SP-NDK dependencies (SP-NDK only)
             'libui',
             'libutils',
@@ -552,7 +548,6 @@
         gb.add_multilib(PT_SYSTEM, 'liblog')
         gb.add_multilib(PT_SYSTEM, 'libm')
         gb.add_multilib(PT_SYSTEM, 'libstdc++')
-        gb.add_multilib(PT_SYSTEM, 'libz')
 
         # Add SP-NDK libraries.
         gb.add_multilib(PT_SYSTEM, 'libEGL')
@@ -619,7 +614,6 @@
         self.assertNotIn('/system/lib/liblog.so', vndk_cap)
         self.assertNotIn('/system/lib/libm.so', vndk_cap)
         self.assertNotIn('/system/lib/libstdc++.so', vndk_cap)
-        self.assertNotIn('/system/lib/libz.so', vndk_cap)
 
         self.assertNotIn('/system/lib64/libEGL.so', vndk_cap)
         self.assertNotIn('/system/lib64/libOpenGLES_v2.so', vndk_cap)
@@ -628,7 +622,6 @@
         self.assertNotIn('/system/lib64/liblog.so', vndk_cap)
         self.assertNotIn('/system/lib64/libm.so', vndk_cap)
         self.assertNotIn('/system/lib64/libstdc++.so', vndk_cap)
-        self.assertNotIn('/system/lib64/libz.so', vndk_cap)
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/vndk/tools/definition-tool/tests/test_ndk_libs.py b/vndk/tools/definition-tool/tests/test_ndk_libs.py
index 36c1720..95d58b7 100755
--- a/vndk/tools/definition-tool/tests/test_ndk_libs.py
+++ b/vndk/tools/definition-tool/tests/test_ndk_libs.py
@@ -18,29 +18,34 @@
         self.assertTrue(NDK_LIBS.is_ll_ndk('/system/lib/liblog.so'))
         self.assertTrue(NDK_LIBS.is_ll_ndk('/system/lib/libm.so'))
         self.assertTrue(NDK_LIBS.is_ll_ndk('/system/lib/libstdc++.so'))
-        self.assertTrue(NDK_LIBS.is_ll_ndk('/system/lib/libz.so'))
 
         self.assertTrue(NDK_LIBS.is_ll_ndk('/system/lib64/libc.so'))
         self.assertTrue(NDK_LIBS.is_ll_ndk('/system/lib64/libdl.so'))
         self.assertTrue(NDK_LIBS.is_ll_ndk('/system/lib64/liblog.so'))
         self.assertTrue(NDK_LIBS.is_ll_ndk('/system/lib64/libm.so'))
         self.assertTrue(NDK_LIBS.is_ll_ndk('/system/lib64/libstdc++.so'))
-        self.assertTrue(NDK_LIBS.is_ll_ndk('/system/lib64/libz.so'))
 
-        self.assertFalse(NDK_LIBS.is_ll_ndk('/system/lib/libz'))
-        self.assertFalse(NDK_LIBS.is_ll_ndk('/system/lib/libzz.so'))
+        self.assertFalse(NDK_LIBS.is_ll_ndk('/system/lib/libm'))
+
+        # libz.so is not LL-NDK anymore.
+        self.assertFalse(NDK_LIBS.is_ll_ndk('/system/lib/libz.so'))
+        self.assertFalse(NDK_LIBS.is_ll_ndk('/system/lib64/libz.so'))
 
     def test_is_sp_ndk(self):
         self.assertTrue(NDK_LIBS.is_sp_ndk('/system/lib/libEGL.so'))
         self.assertTrue(NDK_LIBS.is_sp_ndk('/system/lib/libGLESv1_CM.so'))
         self.assertTrue(NDK_LIBS.is_sp_ndk('/system/lib/libGLESv2.so'))
         self.assertTrue(NDK_LIBS.is_sp_ndk('/system/lib/libGLESv3.so'))
+        self.assertTrue(NDK_LIBS.is_sp_ndk('/system/lib/libnativewindow.so'))
+        self.assertTrue(NDK_LIBS.is_sp_ndk('/system/lib/libsync.so'))
         self.assertTrue(NDK_LIBS.is_sp_ndk('/system/lib/libvulkan.so'))
 
         self.assertTrue(NDK_LIBS.is_sp_ndk('/system/lib64/libEGL.so'))
         self.assertTrue(NDK_LIBS.is_sp_ndk('/system/lib64/libGLESv1_CM.so'))
         self.assertTrue(NDK_LIBS.is_sp_ndk('/system/lib64/libGLESv2.so'))
         self.assertTrue(NDK_LIBS.is_sp_ndk('/system/lib64/libGLESv3.so'))
+        self.assertTrue(NDK_LIBS.is_sp_ndk('/system/lib64/libnativewindow.so'))
+        self.assertTrue(NDK_LIBS.is_sp_ndk('/system/lib64/libsync.so'))
         self.assertTrue(NDK_LIBS.is_sp_ndk('/system/lib64/libvulkan.so'))
 
         # Vendor libraries with the same name are still not SP-NDK.
@@ -97,14 +102,12 @@
         self.assertTrue(NDK_LIBS.is_ndk('/system/lib/liblog.so'))
         self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libm.so'))
         self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libstdc++.so'))
-        self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libz.so'))
 
         self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libc.so'))
         self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libdl.so'))
         self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/liblog.so'))
         self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libm.so'))
         self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libstdc++.so'))
-        self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libz.so'))
 
         # SP-NDK
         self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libEGL.so'))
@@ -134,6 +137,10 @@
         self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libmediandk.so'))
         self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libvulkan.so'))
 
+        # libz.so is not NDK anymore.
+        self.assertFalse(NDK_LIBS.is_ndk('/system/lib/libz.so'))
+        self.assertFalse(NDK_LIBS.is_ndk('/system/lib64/libz.so'))
+
     def test_classify(self):
         self.assertEqual(NDK_LIBS.NOT_NDK,
                          NDK_LIBS.classify('/system/lib/libfoo.so'))
diff --git a/vndk/tools/definition-tool/tools/remove_dt_needed.py b/vndk/tools/definition-tool/tools/remove_dt_needed.py
new file mode 100755
index 0000000..4340ff2
--- /dev/null
+++ b/vndk/tools/definition-tool/tools/remove_dt_needed.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python3
+
+from __future__ import print_function
+
+import argparse
+import collections
+import struct
+import sys
+
+
+Elf_Hdr = collections.namedtuple(
+        'Elf_Hdr',
+        'ei_class ei_data ei_version ei_osabi e_type e_machine e_version '
+        'e_entry e_phoff e_shoff e_flags e_ehsize e_phentsize e_phnum '
+        'e_shentsize e_shnum e_shstridx')
+
+
+Elf_Shdr = collections.namedtuple(
+        'Elf_Shdr',
+        'sh_name sh_type sh_flags sh_addr sh_offset sh_size sh_link sh_info '
+        'sh_addralign sh_entsize')
+
+
+Elf_Dyn = collections.namedtuple('Elf_Dyn', 'd_tag d_val')
+
+
+class Elf_Sym(collections.namedtuple(
+    'ELF_Sym', 'st_name st_value st_size st_info st_other st_shndx')):
+
+    STB_LOCAL = 0
+    STB_GLOBAL = 1
+    STB_WEAK = 2
+
+    SHN_UNDEF = 0
+
+    @property
+    def st_bind(self):
+        return (self.st_info >> 4)
+
+    @property
+    def is_local(self):
+        return self.st_bind == Elf_Sym.STB_LOCAL
+
+    @property
+    def is_global(self):
+        return self.st_bind == Elf_Sym.STB_GLOBAL
+
+    @property
+    def is_weak(self):
+        return self.st_bind == Elf_Sym.STB_WEAK
+
+    @property
+    def is_undef(self):
+        return self.st_shndx == Elf_Sym.SHN_UNDEF
+
+
+class ELFError(ValueError):
+    pass
+
+
+# ELF file format constants.
+ELF_MAGIC = b'\x7fELF'
+
+EI_CLASS = 4
+EI_DATA = 5
+
+ELFCLASS32 = 1
+ELFCLASS64 = 2
+ELFDATA2LSB = 1
+ELFDATA2MSB = 2
+
+DT_NULL = 0
+DT_NEEDED = 1
+
+
+if sys.version_info >= (3, 0):
+    def _extract_buf_byte(buf, offset):
+        return buf[offset]
+else:
+    def _extract_buf_byte(buf, offset):
+        return ord(buf[offset])
+
+
+def _extract_zero_terminated_buf_slice(buf, offset):
+    """Extract a zero-terminated buffer slice from the given offset"""
+    end = offset
+    try:
+        while _extract_buf_byte(buf, end) != 0:
+            end += 1
+    except IndexError:
+        pass
+    return buf[offset:end]
+
+
+if sys.version_info >= (3, 0):
+    def _extract_zero_terminated_str(buf, offset):
+        buf_slice = _extract_zero_terminated_buf_slice(buf, offset)
+        return buf_slice.decode('utf-8')
+else:
+    def _extract_zero_terminated_str(buf, offset):
+        return _extract_zero_terminated_buf_slice(buf, offset)
+
+
+def _replace_dt_needed_buf_internal(buf, dt_needed_name):
+    # Check ELF ident.
+    if len(buf) < 8:
+        raise ELFError('bad ident')
+
+    if buf[0:4] != ELF_MAGIC:
+        raise ELFError('bad magic')
+
+    ei_class = _extract_buf_byte(buf, EI_CLASS)
+    if ei_class not in (ELFCLASS32, ELFCLASS64):
+        raise ELFError('unknown word size')
+    is_32bit = ei_class == ELFCLASS32
+
+    ei_data = _extract_buf_byte(buf, EI_DATA)
+    if ei_data not in (ELFDATA2LSB, ELFDATA2MSB):
+        raise ELFError('unknown endianness')
+
+    # ELF structure definitions.
+    endian_fmt = '<' if ei_data == ELFDATA2LSB else '>'
+
+    if is_32bit:
+        elf_hdr_fmt = endian_fmt + '4x4B8xHHLLLLLHHHHHH'
+        elf_shdr_fmt = endian_fmt + 'LLLLLLLLLL'
+        elf_dyn_fmt = endian_fmt + 'lL'
+        elf_sym_fmt = endian_fmt + 'LLLBBH'
+    else:
+        elf_hdr_fmt = endian_fmt + '4x4B8xHHLQQQLHHHHHH'
+        elf_shdr_fmt = endian_fmt + 'LLQQQQLLQQ'
+        elf_dyn_fmt = endian_fmt + 'QQ'
+        elf_sym_fmt = endian_fmt + 'LBBHQQ'
+
+    def parse_struct(cls, fmt, offset, error_msg):
+        try:
+            return cls._make(struct.unpack_from(fmt, buf, offset))
+        except struct.error:
+            raise ELFError(error_msg)
+
+    def parse_elf_hdr(offset):
+        return parse_struct(Elf_Hdr, elf_hdr_fmt, offset, 'bad elf header')
+
+    def parse_elf_shdr(offset):
+        return parse_struct(Elf_Shdr, elf_shdr_fmt, offset,
+                            'bad section header')
+
+    def parse_elf_dyn(offset):
+        return parse_struct(Elf_Dyn, elf_dyn_fmt, offset, 'bad .dynamic entry')
+
+    def extract_str(offset):
+        return _extract_zero_terminated_str(buf, offset)
+
+    # Parse ELF header.
+    header = parse_elf_hdr(0)
+
+    # Check section header size.
+    if header.e_shentsize == 0:
+        raise ELFError('no section header')
+
+    # Find .shstrtab section.
+    shstrtab_shdr_off = \
+            header.e_shoff + header.e_shstridx * header.e_shentsize
+    shstrtab_shdr = parse_elf_shdr(shstrtab_shdr_off)
+    shstrtab_off = shstrtab_shdr.sh_offset
+
+    # Parse ELF section header.
+    sections = dict()
+    header_end = header.e_shoff + header.e_shnum * header.e_shentsize
+    for shdr_off in range(header.e_shoff, header_end, header.e_shentsize):
+        shdr = parse_elf_shdr(shdr_off)
+        name = extract_str(shstrtab_off + shdr.sh_name)
+        sections[name] = shdr
+
+    # Find .dynamic and .dynstr section header.
+    dynamic_shdr = sections.get('.dynamic')
+    if not dynamic_shdr:
+        raise ELFError('no .dynamic section')
+
+    dynstr_shdr = sections.get('.dynstr')
+    if not dynstr_shdr:
+        raise ELFError('no .dynstr section')
+
+    dynamic_off = dynamic_shdr.sh_offset
+    dynstr_off = dynstr_shdr.sh_offset
+
+    # Find DT_NULL entry.
+    ent_size = dynamic_shdr.sh_entsize
+    assert struct.calcsize(elf_dyn_fmt) == ent_size
+
+    if dynamic_shdr.sh_size < ent_size:
+        raise ELFError('.dynamic section is empty')
+
+    dynamic_end = dynamic_off + dynamic_shdr.sh_size
+    dt_null_off = dynamic_end - ent_size
+    if parse_elf_dyn(dt_null_off).d_tag != DT_NULL:
+        raise ELFError('.dynamic section is not ended with DT_NULL')
+    dt_null_ent = buf[dt_null_off:dt_null_off + ent_size]
+
+    # Build result buffer which replaces matching DT_NEEDED entries.
+    res = buf[0:dynamic_off]
+    for ent_off in range(dynamic_off, dynamic_end, ent_size):
+        ent = parse_elf_dyn(ent_off)
+        if ent.d_tag != DT_NEEDED or \
+                extract_str(dynstr_off + ent.d_val) != dt_needed_name:
+            res += buf[ent_off:ent_off + ent_size]
+    for ent_off in range(len(res), dynamic_end, ent_size):
+        res += dt_null_ent
+    res += buf[dynamic_end:]
+    return res
+
+
+def replace_dt_needed_buf(buf, dt_needed_name):
+    try:
+        return _replace_dt_needed_buf_internal(buf, dt_needed_name)
+    except IndexError:
+        raise ELFError('bad offset')
+
+
+def replace_dt_needed(input_path, output_path, dt_needed_name):
+    with open(input_path, 'rb') as f:
+        buf = f.read()
+
+    buf = replace_dt_needed_buf(buf, dt_needed_name)
+
+    with open(output_path, 'wb') as f:
+        f.write(buf)
+
+
+def main():
+    parser = argparse.ArgumentParser(description='Remove DT_NEEDED entries')
+    parser.add_argument('input', help='input ELF file')
+    parser.add_argument('--output', '-o', required=True, help='output ELF file')
+    parser.add_argument('--name', required=True, help='name')
+    args = parser.parse_args()
+
+    try:
+        replace_dt_needed(args.input, args.output, args.name)
+        return 0
+    except (OSError, ELFError) as e:
+        print('error:', e, file=sys.stderr)
+
+    return 1
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/vndk/tools/definition-tool/vndk_definition_tool.py b/vndk/tools/definition-tool/vndk_definition_tool.py
index d59275a..37d378c 100755
--- a/vndk/tools/definition-tool/vndk_definition_tool.py
+++ b/vndk/tools/definition-tool/vndk_definition_tool.py
@@ -491,7 +491,6 @@
         'liblog.so',
         'libm.so',
         'libstdc++.so',
-        'libz.so',
     )
 
     SP_NDK_LIB_NAMES = (
@@ -499,6 +498,8 @@
         'libGLESv1_CM.so',
         'libGLESv2.so',
         'libGLESv3.so',
+        'libnativewindow.so',
+        'libsync.so',
         'libvulkan.so',
     )
 
@@ -999,8 +1000,6 @@
             '^.*/libhidltransport\\.so$',
             '^.*/libhwbinder\\.so$',
             '^.*/liblzma\\.so$',
-            '^.*/libnativewindow\\.so$',
-            '^.*/libsync\\.so$',
 
             # SP-NDK VNDK-stable (should to be removed)
             '^.*/libbacktrace\\.so$',
@@ -1017,6 +1016,9 @@
             '^.*/libhardware\\.so$',
             '^.*/libnativeloader\\.so$',
             '^.*/libvintf\\.so$',
+
+            # Other libraries.
+            '^.*/libz\\.so$',
         )
 
         return self.compute_path_matched_lib(path_patterns)