Skip to content

Commit 7f88025

Browse files
authored
Merge pull request #514 from S2E/issue/513-nativecompiler
Use system compilers instead of custom ones to build s2e
2 parents 8ed2255 + 1a22a9a commit 7f88025

14 files changed

Lines changed: 648 additions & 1823 deletions

File tree

pylint_rc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
# Add files or directories to the blacklist. They should be base names, not
1111
# paths.
12-
ignore=CVS,TraceEntries_pb2.py
12+
ignore=CVS
13+
ignore-patterns=.*_pb2\.py
1314

1415
# Pickle collected data for later comparisons.
1516
persistent=yes

pyproject.toml

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
[build-system]
2+
requires = ["setuptools>=45", "wheel"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "s2e-env"
7+
description = "A command-line tool for administering S2E environments"
8+
authors = [
9+
{name = "Adrian Herrera", email = "adrian.herrera@epfl.ch"}
10+
]
11+
readme = "README.md"
12+
license = {text = "MIT"}
13+
requires-python = ">=3.6"
14+
classifiers = [
15+
"Intended Audience :: Developers",
16+
"Environment :: Console",
17+
"Programming Language :: Python",
18+
"Topic :: Security",
19+
"Topic :: Software Development",
20+
"Topic :: System",
21+
]
22+
dependencies = [
23+
# S2E engine requirements
24+
"docutils==0.22.2",
25+
"pygments==2.19.2",
26+
# s2e-env requirements
27+
"distro==1.9.0",
28+
"enum34==1.1.10",
29+
"jinja2==3.1.6",
30+
"pefile==2019.4.18",
31+
"psutil==7.1.0",
32+
"pyelftools @ git+https://github.com/S2E/pyelftools.git#egg=pyelftools-0.24+s2e",
33+
"python-magic==0.4.27",
34+
"pyyaml==6.0.2",
35+
"requests==2.32.5",
36+
"sh==2.2.2",
37+
"termcolor==3.1.0",
38+
"pytrie==0.4.0",
39+
"pwntools==4.3.1",
40+
"protobuf3-to-dict==0.1.5",
41+
"immutables==0.21",
42+
"alive_progress==3.3.0",
43+
"protobuf==3.20.2",
44+
# Dependencies for symchk
45+
# TODO: replace with official release when available
46+
# "pdbparse @ git+https://github.com/faratech/pdbparse.git@5eeff2bc11a5063f130216f929706631e8302be2",
47+
"pefile==2019.4.18",
48+
"construct==2.10.70",
49+
"patool==1.12",
50+
"pyunpack==0.2.2",
51+
# FTP server used by image creation
52+
"pyftpdlib==2.0.1"
53+
]
54+
55+
dynamic = ["version"]
56+
57+
[project.urls]
58+
Homepage = "http://s2e.systems"
59+
Repository = "https://github.com/S2E/s2e-env.git"
60+
61+
[project.optional-dependencies]
62+
test = ["pytest"]
63+
64+
[project.scripts]
65+
s2e = "s2e_env.manage:main"
66+
67+
[tool.setuptools]
68+
include-package-data = true
69+
70+
[tool.setuptools.packages.find]
71+
# This will automatically find all packages
72+
73+
[tool.setuptools.package-data]
74+
s2e_env = [
75+
"templates/*",
76+
"templates/plugin_creation/*",
77+
"dat/*",
78+
]
79+
80+
[tool.setuptools.dynamic]
81+
version = {file = "s2e_env/dat/VERSION"}

s2e_env/commands/build.py

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,25 @@
2929
import sh
3030
from sh import ErrorReturnCode
3131

32+
from s2e_env import CONSTANTS
3233
from s2e_env.command import EnvCommand, CommandError
33-
34+
from s2e_env.utils.host import get_os_version
3435

3536
logger = logging.getLogger('build')
3637

3738

39+
def _get_clang_version():
40+
default = CONSTANTS["clang_versions"]["default"]
41+
os_name, os_version = get_os_version()
42+
version = CONSTANTS["clang_versions"].get(f"{os_name}-{os_version}", None)
43+
if version:
44+
logger.info('Using clang-%s to build S2E', version)
45+
return version
46+
47+
logger.info('Using clang-%s to build S2E', default)
48+
return default
49+
50+
3851
class Command(EnvCommand):
3952
"""
4053
Builds S2E.
@@ -60,20 +73,53 @@ def add_arguments(self, parser):
6073
help='List of S2E components to clean prior to '
6174
'the build process')
6275

63-
def handle(self, *args, **options):
76+
def _build_tools(self, build_dir):
77+
makefile = self.env_path('source', 's2e', 'Makefile.tools')
78+
if not os.path.isfile(makefile):
79+
raise CommandError(f'{makefile} not found')
80+
81+
try:
82+
logger.info('Generating S2E build environment Docker image')
83+
docker = sh.Command('docker')
84+
85+
docker(
86+
"build",
87+
"--target", "s2e-build-env",
88+
"-t", "s2e-build-env",
89+
".",
90+
_out=sys.stdout,
91+
_err=sys.stderr,
92+
_cwd=self.env_path('source', 's2e')
93+
)
94+
95+
logger.info('Building S2E tools in %s', build_dir)
96+
docker(
97+
"run", "-t", "--rm",
98+
"-e", "SYSTEM_CLANG_VERSION=15",
99+
"-e", f"S2E_PREFIX={self.install_path()}",
100+
"-w", build_dir,
101+
"-v", f"{self.env_path()}:{self.env_path()}",
102+
"s2e-build-env",
103+
"/run_as.sh", os.getuid(), os.getgid(),
104+
"make", "-f", makefile, "install",
105+
_out=sys.stdout,
106+
_err=sys.stderr
107+
)
108+
except ErrorReturnCode as e:
109+
raise CommandError(e) from e
110+
111+
logger.success('S2E tools built')
112+
113+
def _build_s2e(self, build_dir, **options):
64114
# Exit if the makefile doesn't exist
65115
makefile = self.env_path('source', 'Makefile')
66116
if not os.path.isfile(makefile):
67-
raise CommandError(f'No makefile found in {os.path.dirname(makefile)}')
68-
69-
# If the build directory doesn't exist, create it
70-
build_dir = self.env_path('build')
71-
if not os.path.isdir(build_dir):
72-
os.mkdir(build_dir)
117+
raise CommandError(f'{makefile} not found')
73118

74119
# Set up some environment variables
75120
env_vars = os.environ.copy()
76121
env_vars['S2E_PREFIX'] = self.install_path()
122+
env_vars['SYSTEM_CLANG_VERSION'] = f"{_get_clang_version()}"
77123

78124
components = options['components']
79125
self._make = sh.Command('make').bake(directory=build_dir, file=makefile, _env=env_vars)
@@ -96,6 +142,15 @@ def handle(self, *args, **options):
96142

97143
logger.success('S2E built')
98144

145+
def handle(self, *args, **options):
146+
# If the build directory doesn't exist, create it
147+
build_dir = self.env_path('build')
148+
if not os.path.isdir(build_dir):
149+
os.mkdir(build_dir)
150+
151+
self._build_tools(build_dir)
152+
self._build_s2e(build_dir, **options)
153+
99154
def _get_components(self, target):
100155
lines = self._make(target).strip().split('\n')
101156
print(lines)

s2e_env/commands/code_coverage/lcov.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import logging
2626
import os
27+
import re
2728
import sys
2829

2930
# pylint: disable=no-name-in-module
@@ -93,7 +94,17 @@ def _get_addr_coverage(directory, aggregated_coverage):
9394
return aggregated_coverage
9495

9596

96-
def _save_coverage_info(lcov_path, file_line_info, ignore_missing_files):
97+
def _is_excluded(file_path, excluded_patterns):
98+
if not excluded_patterns:
99+
return False
100+
101+
if any(re.search(pattern, file_path) for pattern in excluded_patterns):
102+
return True
103+
104+
return False
105+
106+
107+
def _save_coverage_info(lcov_path, file_line_info, ignore_missing_files, excluded_patterns):
97108
"""
98109
Save the line coverage information in lcov format.
99110
@@ -121,6 +132,10 @@ def _save_coverage_info(lcov_path, file_line_info, ignore_missing_files):
121132
num_non_zero_lines = 0
122133
num_instrumented_lines = 0
123134

135+
if _is_excluded(src_file, excluded_patterns):
136+
logger.info('Excluding %s from coverage report', src_file)
137+
continue
138+
124139
f.write(f'SF:{src_file}\n')
125140
for line, count in file_line_info[src_file].items():
126141
f.write(f'DA:{line},{count}\n')
@@ -177,14 +192,15 @@ def handle(self, *args, **options):
177192
for module_path, addr_counts in coverage.items():
178193
try:
179194
cov = options.get('include_covered_files_only', False)
195+
excluded_patterns = options.get('exclude_pattern', [])
180196
file_line_info = syms.get_coverage(module_path, addr_counts, cov)
181197

182198
module = os.path.basename(module_path)
183199

184200
# genhtml will throw an error if there are missing files, so we skip them
185201
# if the user enabled html reports.
186202
lcov_info_path = os.path.join(lcov_out_dir, module + '.info')
187-
_save_coverage_info(lcov_info_path, file_line_info, do_gen_html)
203+
_save_coverage_info(lcov_info_path, file_line_info, do_gen_html, excluded_patterns)
188204

189205
logger.success('Line coverage saved to %s', lcov_info_path)
190206

s2e_env/commands/coverage.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ def add_arguments(self, parser):
5353
help='S2E output directories to consider (default is s2e-last).',
5454
required=False)
5555

56+
lcov_parser.add_argument('--exclude-pattern', action='append',
57+
help='Exclude the given pattern from the report. Can be specified multiple times.',
58+
required=False)
59+
5660
lcov_parser.add_argument('--aggregate-outputs', type=int,
5761
help='Specifies n most recent s2e-out-* folders to aggregate for coverage.',
5862
required=False)

s2e_env/commands/deps.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from s2e_env.command import BaseCommand
2+
from s2e_env.commands.init import install_dependencies
3+
4+
class Command(BaseCommand):
5+
"""
6+
Installs required system packages to build and run S2E.
7+
"""
8+
9+
help = 'Install required system packages for S2E'
10+
11+
def handle(self, *args, **options):
12+
install_dependencies()

s2e_env/commands/init.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def _compute_dependencies():
101101
return all_install_packages
102102

103103

104-
def _install_dependencies():
104+
def install_dependencies():
105105
"""
106106
Install S2E's dependencies.
107107
@@ -122,7 +122,7 @@ def _install_dependencies():
122122
install_packages.append(package)
123123

124124
install_opts = ['--no-install-recommends']
125-
env = {}
125+
env = os.environ.copy()
126126

127127
env['DEBIAN_FRONTEND'] = 'noninteractive'
128128
install_opts = ['-y'] + install_opts
@@ -285,7 +285,8 @@ def add_arguments(self, parser):
285285
parser.add_argument('dir', nargs='?', default=os.getcwd(),
286286
help='The environment directory. Defaults to the '
287287
'current working directory')
288-
parser.add_argument('-s', '--skip-dependencies', action='store_true',
288+
parser.add_argument('--install-dependencies', action='store_true',
289+
required=False, default=True,
289290
help='Skip the dependency install via apt')
290291
parser.add_argument('-b', '--use-existing-install', required=False,
291292
default=None,
@@ -341,8 +342,8 @@ def handle(self, *args, **options):
341342
_link_existing_install(env_path, existing_install_path)
342343
else:
343344
# Install S2E's dependencies via apt-get
344-
if not options['skip_dependencies']:
345-
_install_dependencies()
345+
if options['install_dependencies']:
346+
install_dependencies()
346347

347348
# Get the source repositories
348349
_get_s2e_sources(env_path, options['manifest_branch'])

s2e_env/commands/new_project.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@
5656
CGC_REGEX = re.compile(r'^CGC 32-bit')
5757
ELF32_REGEX = re.compile(r'^ELF 32-bit')
5858
ELF64_REGEX = re.compile(r'^ELF 64-bit')
59-
DLL32_REGEX = re.compile(r'^PE32 executable \(DLL\)')
60-
DLL64_REGEX = re.compile(r'^PE32\+ executable \(DLL\)')
61-
WIN32_DRIVER_REGEX = re.compile(r'^PE32 executable \(native\)')
62-
WIN64_DRIVER_REGEX = re.compile(r'^PE32\+ executable \(native\)')
59+
DLL32_REGEX = re.compile(r'^PE32 executable.*?\(DLL\)')
60+
DLL64_REGEX = re.compile(r'^PE32\+ executable.*?\(DLL\)')
61+
WIN32_DRIVER_REGEX = re.compile(r'^PE32 executable.*?\(native\)')
62+
WIN64_DRIVER_REGEX = re.compile(r'^PE32\+ executable.*?\(native\)')
6363
PE32_REGEX = re.compile(r'^PE32 executable')
6464
PE64_REGEX = re.compile(r'^PE32\+ executable')
6565
MSDOS_REGEX = re.compile(r'^MS-DOS executable')

0 commit comments

Comments
 (0)