From c453400d597116954b3bb2a9e2c3d60251dc97a2 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Mon, 1 Oct 2018 20:48:34 -0400 Subject: [PATCH] Add 'b_pie' compiler option On Android executables must be position independent, many distributions enable it by default too for security reasons. --- docs/markdown/Builtin-options.md | 1 + docs/markdown/Reference-manual.md | 1 + docs/markdown/snippets/pie.md | 6 ++++++ mesonbuild/backend/backends.py | 2 ++ mesonbuild/backend/ninjabackend.py | 2 ++ mesonbuild/build.py | 31 +++++++++++++++++++++++------- mesonbuild/compilers/compilers.py | 23 +++++++++++++++++++++- 7 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 docs/markdown/snippets/pie.md diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index 55d82a51e..ce14304e4 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -82,6 +82,7 @@ platforms or with all compilers: | b_pgo | off | off, generate, use | Use profile guided optimization | | b_sanitize | none | see below | Code sanitizer to use | | b_staticpic | true | true, false | Build static libraries as position independent | +| b_pie | false | true, false | Build position-independent executables (since 0.49.0)| The value of `b_sanitize` can be one of: `none`, `address`, `thread`, `undefined`, `memory`, `address,undefined`. diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index e4c9303e8..64ed9d1a8 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -552,6 +552,7 @@ be passed to [shared and static libraries](#library). - `d_unittest`, when set to true, the D modules are compiled in debug mode - `d_module_versions` list of module version identifiers set when compiling D sources - `d_debug` list of module debug identifiers set when compiling D sources +- `pie` *(added 0.49.0)* build a position-independent executable The list of `sources`, `objects`, and `dependencies` is always flattened, which means you can freely nest and add lists while diff --git a/docs/markdown/snippets/pie.md b/docs/markdown/snippets/pie.md new file mode 100644 index 000000000..a9be1749c --- /dev/null +++ b/docs/markdown/snippets/pie.md @@ -0,0 +1,6 @@ +## Position-independent executables + +When `b_pie` option, or `executable()`'s `pie` keyword argument is set to +`true`, position-independent executables are built. All their objects are built +with `-fPIE` and the executable is linked with `-pie`. Any static library they +link must be built with `pic` set to `true` (see `b_staticpic` option). diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 28ef6705a..f12b357a2 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -552,6 +552,8 @@ class Backend: # Set -fPIC for static libraries by default unless explicitly disabled if isinstance(target, build.StaticLibrary) and target.pic: commands += compiler.get_pic_args() + if isinstance(target, build.Executable) and target.pie: + commands += compiler.get_pie_args() # Add compile args needed to find external dependencies. Link args are # added while generating the link command. # NOTE: We must preserve the order in which external deps are diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index b5643110b..d8370c288 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2245,6 +2245,8 @@ rule FORTRAN_DEP_HACK%s # If implib, and that's significant on this platform (i.e. Windows using either GCC or Visual Studio) if target.import_filename: commands += linker.gen_import_library_args(os.path.join(self.get_target_dir(target), target.import_filename)) + if target.pie: + commands += linker.get_pie_link_args() elif isinstance(target, build.SharedLibrary): if isinstance(target, build.SharedModule): options = self.environment.coredata.base_options diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 8ba5465fc..f2a6d9c23 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -84,7 +84,7 @@ known_build_target_kwargs = ( rust_kwargs | cs_kwargs) -known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic'} +known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'pie'} known_shlib_kwargs = known_build_target_kwargs | {'version', 'soversion', 'vs_module_defs', 'darwin_versions'} known_shmod_kwargs = known_build_target_kwargs known_stlib_kwargs = known_build_target_kwargs | {'pic'} @@ -414,6 +414,8 @@ class BuildTarget(Target): self.generated = [] self.extra_files = [] self.d_features = {} + self.pic = False + self.pie = False # Sources can be: # 1. Pre-existing source files in the source tree # 2. Pre-existing sources generated by configure_file in the build tree @@ -869,13 +871,14 @@ This will become a hard error in a future Meson release.''') # since library loading is done differently) if for_darwin(self.is_cross, self.environment) or for_windows(self.is_cross, self.environment): self.pic = True - elif '-fPIC' in clist + cpplist: - mlog.warning("Use the 'pic' kwarg instead of passing -fPIC manually to static library {!r}".format(self.name)) - self.pic = True else: - self.pic = kwargs.get('pic', False) - if not isinstance(self.pic, bool): - raise InvalidArguments('Argument pic to static library {!r} must be boolean'.format(self.name)) + self.pic = self._extract_pic_pie(kwargs, 'pic') + if isinstance(self, Executable): + # Executables must be PIE on Android + if for_android(self.is_cross, self.environment): + self.pie = True + else: + self.pie = self._extract_pic_pie(kwargs, 'pie') self.implicit_include_directories = kwargs.get('implicit_include_directories', True) if not isinstance(self.implicit_include_directories, bool): raise InvalidArguments('Implicit_include_directories must be a boolean.') @@ -888,6 +891,18 @@ This will become a hard error in a future Meson release.''') raise InvalidArguments('GNU symbol visibility arg %s not one of: %s', self.symbol_visibility, ', '.join(permitted)) + def _extract_pic_pie(self, kwargs, arg): + # Check if we have -fPIC, -fpic, -fPIE, or -fpie in cflags + all_flags = self.extra_args['c'] + self.extra_args['cpp'] + if '-f' + arg.lower() in all_flags or '-f' + arg.upper() in all_flags: + mlog.warning("Use the '{}' kwarg instead of passing '{}' manually to {!r}".format(arg, '-f' + arg, self.name)) + return True + + val = kwargs.get(arg, False) + if not isinstance(val, bool): + raise InvalidArguments('Argument {} to {!r} must be boolean'.format(arg, self.name)) + return val + def get_filename(self): return self.filename @@ -1307,6 +1322,8 @@ class Executable(BuildTarget): known_kwargs = known_exe_kwargs def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): + if 'pie' not in kwargs and 'b_pie' in environment.coredata.base_options: + kwargs['pie'] = environment.coredata.base_options['b_pie'].value super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs) # Unless overridden, executables have no suffix or prefix. Except on # Windows and with C#/Mono executables where the suffix is 'exe' diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 26eec48c3..50fda5a0d 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -331,6 +331,9 @@ base_options = {'b_pch': coredata.UserBooleanOption('b_pch', 'Use precompiled he 'b_staticpic': coredata.UserBooleanOption('b_staticpic', 'Build static libraries as position independent', True), + 'b_pie': coredata.UserBooleanOption('b_pie', + 'Build executables as position independent', + False), 'b_bitcode': coredata.UserBooleanOption('b_bitcode', 'Generate and embed bitcode (only macOS and iOS)', False), @@ -1180,6 +1183,18 @@ class Compiler: raise EnvironmentException( 'Language {} does not support function attributes.'.format(self.get_display_language())) + def get_pic_args(self): + m = 'Language {} does not support position-independent code' + raise EnvironmentException(m.format(self.get_display_language())) + + def get_pie_args(self): + m = 'Language {} does not support position-independent executable' + raise EnvironmentException(m.format(self.get_display_language())) + + def get_pie_link_args(self): + m = 'Language {} does not support position-independent executable' + raise EnvironmentException(m.format(self.get_display_language())) + @enum.unique class CompilerType(enum.Enum): @@ -1322,7 +1337,7 @@ class GnuLikeCompiler(abc.ABC): def __init__(self, compiler_type): self.compiler_type = compiler_type self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage', - 'b_ndebug', 'b_staticpic'] + 'b_ndebug', 'b_staticpic', 'b_pie'] if not self.compiler_type.is_osx_compiler and not self.compiler_type.is_windows_compiler: self.base_options.append('b_lundef') if not self.compiler_type.is_windows_compiler: @@ -1345,6 +1360,12 @@ class GnuLikeCompiler(abc.ABC): return [] # On Window and OS X, pic is always on. return ['-fPIC'] + def get_pie_args(self): + return ['-fPIE'] + + def get_pie_link_args(self): + return ['-pie'] + def get_buildtype_args(self, buildtype): return gnulike_buildtype_args[buildtype]