LIEF/tests/elf/elf_test.py
Laszlo Kiss-Kollar 3550e45f18 Build manylinux1-compliant wheels in Travis
* Temporarily disable Melkor in test suite
The Makefile uses the -executable flag which doesn't work on RHEL5.
Disabling until I find a workaround for this.

* Patch Makefile in Melkor fuzzer
This replaces the `-executable` flag which is not supported by `find` on
CentOS 5.

* Respect CC environment variable
Several tests hard coded /usr/bin/cc which might not exist in some
environments. We first check the CC environment variable and fall back
to the hard coded path if CC is unset.

* Skip tests on GLIBC < 2.17
Some test binaries were linked against GLIBC 2.17. Skip tests which use
this binary if the platform does not have the required GLIBC version.

* Enable ccache in Docker in Travis builds

* Run `auditwheel repair` on the produced wheels
This will vendor the needed external shared libraries into the wheel and
tag it as manylinux1.

* Install ccache in Docker image

* Avoid using bind mount volume in Docker build

The bind mount volume wrote files as 'root' which causes issues with the
deploy script in Travis. Copying the source code into the image and
retrieving the built wheels instead of mounting the source tree fixes
this issue.

* Fix missing build folder when building with Docker

After finishing the build inside Docker we need the build directory from
the container to be able to deploy the built artifacts with deploy.sh.

* Use the right Python interpreter for Linux builds

The Dockerized .travis.yml builds attempt to invoke the interpreter in
the PYTHON_BINARY environment variable, which is only valid inside the
Docker image. To fix this, override the variable on Linux for tasks
which require the host's Python interpreter.

* Fix missing pip installation in Travis

The Ubuntu image in Travis does not come with `pip` preinstalled for
Python 3.

* Remove .git directory from .dockerignore

As `setup.py` uses `git` to determine the version number we need to copy
the contents of `.git` into the image.
2019-10-02 07:49:45 +02:00

293 lines
10 KiB
Python

#!/usr/bin/env python
import unittest
import lief
import tempfile
import sys
import subprocess
import stat
import os
import logging
import random
import itertools
from lief import Logger
Logger.set_level(lief.LOGGING_LEVEL.WARNING)
#Logger.set_level(lief.LOGGING_LEVEL.DEBUG)
from subprocess import Popen
from unittest import TestCase
from utils import get_sample, has_recent_glibc
class TestELF(TestCase):
def setUp(self):
self.logger = logging.getLogger(__name__)
def test_rpath(self):
etterlog = lief.parse(get_sample('ELF/ELF64_x86-64_binary_etterlog.bin'))
dynamic_entries = etterlog.dynamic_entries
rpath = [e for e in dynamic_entries if e.tag == lief.ELF.DYNAMIC_TAGS.RPATH]
self.assertEqual(len(rpath), 1)
rpath = rpath.pop()
self.assertEqual(rpath.name, "/usr/lib")
def test_runpath(self):
etterlog = lief.parse(get_sample('ELF/ELF64_x86-64_binary_systemd-resolve.bin'))
dynamic_entries = etterlog.dynamic_entries
runpath = [e for e in dynamic_entries if e.tag == lief.ELF.DYNAMIC_TAGS.RUNPATH]
self.assertEqual(len(runpath), 1)
runpath = runpath.pop()
self.assertEqual(runpath.name, "/usr/lib/systemd")
def test_gnuhash(self):
ls = lief.parse(get_sample('ELF/ELF64_x86-64_binary_ls.bin'))
gnu_hash = ls.gnu_hash
self.assertEqual(gnu_hash.nb_buckets, 33)
self.assertEqual(gnu_hash.symbol_index, 109)
self.assertEqual(gnu_hash.shift2, 7)
bloom_filters = gnu_hash.bloom_filters
self.assertEqual(len(bloom_filters), 2)
self.assertIn(0x3FAE01120C48A1A6, bloom_filters)
self.assertIn(0x900004A81310D428, bloom_filters)
buckets = gnu_hash.buckets
self.assertEqual(len(buckets), 33)
buckets_test = [109, 110, 0, 0, 0, 0, 0, 111, 113, 114, 0, 0, 0, 115, 0, 116, 0, 0, 117, 118, 119, 0, 120, 0, 0, 121, 123, 124, 126, 128, 129, 130, 0]
self.assertEqual(buckets_test, buckets)
hash_values = gnu_hash.hash_values
hash_values_test = [0x60E0C78D, 0xF54162E5, 0x7FFD8E4E, 0x1C8BF239, 0xEEFD3EB, 0x1C8C1D29, 0x1C5871D9,
0x5B7F3E03, 0x759A6A7F, 0xEF18DB9, 0xBA53E4D, 0x9789A097, 0x9E7650BC, 0xD39AD3D,
0x12F7C433, 0xEB01FAB6, 0xECD54543, 0xAD3C9892, 0x72632CCF, 0x12F7A2B3, 0x7C92E3BB, 0x7C96F087]
self.assertEqual(hash_values, hash_values_test)
#for s in list(ls.dynamic_symbols)[gnu_hash.symbol_index:]:
# print(gnu_hash.check(s.name), s.name)
self.assertTrue(all(gnu_hash.check(x.name) for x in list(ls.dynamic_symbols)[gnu_hash.symbol_index:]))
self.assertFalse(gnu_hash.check("foofdsfdsfds"))
self.assertFalse(gnu_hash.check("fazertrvkdfsrezklqpfjeopqdi"))
@unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version")
def test_permutation(self):
samples = [
"ELF/ELF64_x86-64_binary_ls.bin",
#"ELF/ELF64_x86-64_binary_gcc.bin",
#"ELF/ELF64_x86-64_binary_openssl.bin",
]
tmp_dir = tempfile.mkdtemp(suffix='_lief_test_permutation')
for sample in samples:
binary = lief.parse(get_sample(sample))
dynamic_symbols = binary.dynamic_symbols
gnu_hash_table = binary.gnu_hash
idx = gnu_hash_table.symbol_index
permutation = [i for i in range(1, len(dynamic_symbols))]
random.shuffle(permutation)
permutation = [0] + permutation
binary.permute_dynamic_symbols(permutation)
builder = lief.ELF.Builder(binary)
builder.empties_gnuhash(True)
builder.build()
output = os.path.join(tmp_dir, "{}.permutated".format(binary.name))
self.logger.debug("Output: {}".format(output))
builder.write(output)
if not sys.platform.startswith("linux"):
return
st = os.stat(output)
os.chmod(output, st.st_mode | stat.S_IEXEC)
p = Popen([output, "--help"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, _ = p.communicate()
self.logger.debug(stdout.decode("utf8"))
self.assertEqual(p.returncode, 0)
def test_notes(self):
systemd_resolve = lief.parse(get_sample('ELF/ELF64_x86-64_binary_systemd-resolve.bin'))
notes = systemd_resolve.notes
self.assertEqual(len(notes), 3)
n1 = notes[0]
n2 = notes[1]
n3 = notes[2]
self.assertEqual(n1.name, "GNU")
self.assertEqual(n2.name, "GNU")
self.assertEqual(n3.name, "GNU")
self.assertEqual(n1.type, lief.ELF.NOTE_TYPES.ABI_TAG)
self.assertEqual(n2.type, lief.ELF.NOTE_TYPES.BUILD_ID)
self.assertEqual(n3.type, lief.ELF.NOTE_TYPES.GOLD_VERSION)
self.assertEqual(n1.details.abi, lief.ELF.NOTE_ABIS.LINUX)
self.assertEqual(n1.details.version, [2, 6, 32])
self.assertEqual(list(n2.description), [
0x7e, 0x68, 0x6c, 0x7d,
0x79, 0x9b, 0xa4, 0xcd,
0x32, 0xa2, 0x34, 0xe8,
0x4f, 0xd7, 0x45, 0x98,
0x21, 0x32, 0x9d, 0xc8
])
self.assertEqual("".join(map(chr, n3.description)), "gold 1.12")
def test_symbols_access(self):
hello = lief.parse(get_sample('ELF/ELF64_x86-64_binary_hello-gdb.bin'))
symbols = hello.symbols
dynamic_symbols = hello.dynamic_symbols
static_symbols = hello.static_symbols
self.assertTrue(all(s in symbols for s in dynamic_symbols))
self.assertTrue(all(s in symbols for s in static_symbols))
def test_relocation_size(self):
aarch64_toybox = lief.parse(get_sample('ELF/ELF64_AARCH64_piebinary_toybox.pie'))
arm_ls = lief.parse(get_sample('ELF/ELF32_ARM_binary_ls.bin'))
x86_ls = lief.parse(get_sample('ELF/ELF32_x86_binary_ls.bin'))
x86_64_ls = lief.parse(get_sample('ELF/ELF64_x86-64_binary_ld.bin'))
for r in itertools.chain(aarch64_toybox.dynamic_relocations, aarch64_toybox.pltgot_relocations):
if lief.ELF.RELOCATION_AARCH64(r.type) == lief.ELF.RELOCATION_AARCH64.RELATIVE:
self.assertEqual(r.size, 64)
if lief.ELF.RELOCATION_AARCH64(r.type) == lief.ELF.RELOCATION_AARCH64.GLOB_DAT:
self.assertEqual(r.size, 64)
if lief.ELF.RELOCATION_AARCH64(r.type) == lief.ELF.RELOCATION_AARCH64.JUMP_SLOT:
self.assertEqual(r.size, 64)
for r in itertools.chain(arm_ls.dynamic_relocations, arm_ls.pltgot_relocations):
if lief.ELF.RELOCATION_ARM(r.type) == lief.ELF.RELOCATION_ARM.RELATIVE:
self.assertEqual(r.size, 32)
if lief.ELF.RELOCATION_ARM(r.type) == lief.ELF.RELOCATION_ARM.GLOB_DAT:
self.assertEqual(r.size, 32)
if lief.ELF.RELOCATION_ARM(r.type) == lief.ELF.RELOCATION_ARM.ABS32:
self.assertEqual(r.size, 32)
if lief.ELF.RELOCATION_ARM(r.type) == lief.ELF.RELOCATION_ARM.JUMP_SLOT:
self.assertEqual(r.size, 32)
for r in itertools.chain(x86_ls.dynamic_relocations, x86_ls.pltgot_relocations):
if lief.ELF.RELOCATION_i386(r.type) == lief.ELF.RELOCATION_i386.GLOB_DAT:
self.assertEqual(r.size, 32)
if lief.ELF.RELOCATION_i386(r.type) == lief.ELF.RELOCATION_i386.COPY:
self.assertEqual(r.size, 32)
if lief.ELF.RELOCATION_i386(r.type) == lief.ELF.RELOCATION_i386.JUMP_SLOT:
self.assertEqual(r.size, 32)
for r in itertools.chain(x86_64_ls.dynamic_relocations, x86_64_ls.pltgot_relocations):
if lief.ELF.RELOCATION_X86_64(r.type) == lief.ELF.RELOCATION_X86_64.GLOB_DAT:
self.assertEqual(r.size, 64)
if lief.ELF.RELOCATION_X86_64(r.type) == lief.ELF.RELOCATION_X86_64.COPY:
self.assertEqual(r.size, 32)
if lief.ELF.RELOCATION_X86_64(r.type) == lief.ELF.RELOCATION_X86_64.JUMP_SLOT:
self.assertEqual(r.size, 64)
def test_sectionless(self):
sample = "ELF/ELF64_x86-64_binary_rvs.bin"
rvs = lief.parse(get_sample(sample))
dynsym = list(rvs.dynamic_symbols)
self.assertEqual(len(dynsym), 10)
def test_dynamic_flags(self):
sample = "ELF/ELF32_ARM_binary_ls.bin"
ls = lief.parse(get_sample(sample))
d_flags = ls.get(lief.ELF.DYNAMIC_TAGS.FLAGS)
d_flags_1 = ls.get(lief.ELF.DYNAMIC_TAGS.FLAGS_1)
self.assertIn(lief.ELF.DYNAMIC_FLAGS.BIND_NOW, d_flags)
self.assertIn(lief.ELF.DYNAMIC_FLAGS_1.NOW, d_flags_1)
def test_unwind_arm(self):
sample = "ELF/ELF32_ARM_binary_ls.bin"
ls = lief.parse(get_sample(sample))
functions = sorted(ls.functions, key=lambda f: f.address)
self.assertEqual(len(functions), 265)
self.assertEqual(functions[0].address, 19684)
self.assertEqual(functions[0].size, 0)
self.assertEqual(functions[0].name, "open")
self.assertEqual(functions[-1].address, 102372)
self.assertEqual(functions[-1].size, 0)
self.assertEqual(functions[-1].name, "")
def test_unwind_x86(self):
sample = "ELF/ELF64_x86-64_binary_ld.bin"
ld = lief.parse(get_sample(sample))
functions = sorted(ld.functions, key=lambda f: f.address)
self.assertEqual(len(functions), 503)
self.assertEqual(functions[0].address, 4209304)
self.assertEqual(functions[0].size, 0)
self.assertEqual(functions[0].name, "_init")
self.assertEqual(functions[10].size, 174)
self.assertEqual(functions[10].name, "")
self.assertEqual(functions[-1].address, 4409396)
self.assertEqual(functions[-1].size, 0)
self.assertEqual(functions[-1].name, "_fini")
def test_misc(self):
sample = "ELF/ELF64_x86-64_binary_ld.bin"
ld = lief.parse(get_sample(sample))
text = ld.get_section(".text")
self.assertFalse(ld.has_section_with_offset(0))
self.assertFalse(ld.has_section_with_va(0xFFFFFFFF))
self.assertTrue(ld.has_section_with_offset(text.offset + 10))
self.assertTrue(ld.has_section_with_va(text.virtual_address + 10))
if __name__ == '__main__':
root_logger = logging.getLogger()
root_logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
root_logger.addHandler(ch)
unittest.main(verbosity=2)