| Index: build/android/pylib/linker/test_case.py
|
| diff --git a/build/android/pylib/linker/test_case.py b/build/android/pylib/linker/test_case.py
|
| index b8a256676341f90169677342d5c8983e293566ce..3f8ea73212be91eaeedff3d781fe591e4b8dd484 100644
|
| --- a/build/android/pylib/linker/test_case.py
|
| +++ b/build/android/pylib/linker/test_case.py
|
| @@ -42,6 +42,7 @@ import subprocess
|
| import tempfile
|
| import time
|
|
|
| +from pylib import constants
|
| from pylib import android_commands
|
| from pylib import flag_changer
|
| from pylib.base import base_test_result
|
| @@ -52,6 +53,17 @@ _PACKAGE_NAME='org.chromium.content_linker_test_apk'
|
| _ACTIVITY_NAME='.ContentLinkerTestActivity'
|
| _COMMAND_LINE_FILE='/data/local/tmp/content-linker-test-command-line'
|
|
|
| +# Path to the Linker.java source file.
|
| +_LINKER_JAVA_SOURCE_PATH = \
|
| + 'content/public/android/java/src/org/chromium/content/app/Linker.java'
|
| +
|
| +# A regular expression used to extract the browser shared RELRO configuration
|
| +# from the Java source file above.
|
| +_RE_LINKER_BROWSER_CONFIG = \
|
| + re.compile(r'.*BROWSER_SHARED_RELRO_CONFIG\s+=\s+' + \
|
| + 'BROWSER_SHARED_RELRO_CONFIG_(\S+)\s*;.*',
|
| + re.MULTILINE | re.DOTALL)
|
| +
|
| # Logcat filters used during each test. Only the 'chromium' one is really
|
| # needed, but the logs are added to the TestResult in case of error, and
|
| # it is handy to have the 'content_android_linker' ones as well when
|
| @@ -67,6 +79,38 @@ re_library_address = re.compile(
|
| r'(BROWSER|RENDERER)_LIBRARY_ADDRESS: (\S+) ([0-9A-Fa-f]+)')
|
|
|
|
|
| +def _GetBrowserSharedRelroConfig():
|
| + """Returns a string corresponding to the Linker's configuration of shared
|
| + RELRO sections in the browser process. This parses the Java linker source
|
| + file to get the appropriate information.
|
| + Return:
|
| + None in case of error (e.g. could not locate the source file).
|
| + 'NEVER' if the browser process shall never use shared RELROs.
|
| + 'LOW_RAM_ONLY' if if uses it only on low-end devices.
|
| + 'ALWAYS' if it always uses a shared RELRO.
|
| + """
|
| + source_path = \
|
| + os.path.join(constants.DIR_SOURCE_ROOT, _LINKER_JAVA_SOURCE_PATH)
|
| + if not os.path.exists(source_path):
|
| + logging.error('Could not find linker source file: ' + source_path)
|
| + return None
|
| +
|
| + with open(source_path) as f:
|
| + configs = _RE_LINKER_BROWSER_CONFIG.findall(f.read())
|
| + if not configs:
|
| + logging.error(
|
| + 'Can\'t find browser shared RELRO configuration value in ' + \
|
| + source_path)
|
| + return None
|
| +
|
| + if configs[0] not in ['NEVER', 'LOW_RAM_ONLY', 'ALWAYS']:
|
| + logging.error('Unexpected browser config value: ' + configs[0])
|
| + return None
|
| +
|
| + logging.info('Found linker browser shared RELRO config: ' + configs[0])
|
| + return configs[0]
|
| +
|
| +
|
| def _WriteCommandLineFile(adb, command_line, command_line_file):
|
| """Create a command-line file on the device. This does not use FlagChanger
|
| because its implementation assumes the device has 'su', and thus does
|
| @@ -401,10 +445,14 @@ class LinkerLibraryAddressTest(LinkerTestCaseBase):
|
| logging.error('Renderer libraries loaded at high addresses: %s', bad_libs)
|
| return ResultType.FAIL, logs
|
|
|
| - if self.is_low_memory:
|
| - # For low-memory devices, the libraries must all be loaded at the same
|
| - # addresses. This also implicitly checks that the browser libraries are at
|
| - # low addresses.
|
| + browser_config = _GetBrowserSharedRelroConfig()
|
| + if not browser_config:
|
| + return ResultType.FAIL, 'Bad linker source configuration'
|
| +
|
| + if browser_config == 'ALWAYS' or \
|
| + (browser_config == 'LOW_RAM_ONLY' and self.is_low_memory):
|
| + # The libraries must all be loaded at the same addresses. This also
|
| + # implicitly checks that the browser libraries are at low addresses.
|
| addr_mismatches = []
|
| for lib_name, lib_address in browser_libs.iteritems():
|
| lib_address2 = renderer_libs[lib_name]
|
| @@ -416,10 +464,10 @@ class LinkerLibraryAddressTest(LinkerTestCaseBase):
|
| addr_mismatches)
|
| return ResultType.FAIL, logs
|
|
|
| - # For regular devices, check that libraries are loaded at 'high-addresses'.
|
| + # Otherwise, check that libraries are loaded at 'high-addresses'.
|
| # Note that for low-memory devices, the previous checks ensure that they
|
| # were loaded at low-addresses.
|
| - if not self.is_low_memory:
|
| + else:
|
| bad_libs = []
|
| for lib_name, lib_address in browser_libs.iteritems():
|
| if lib_address < memory_boundary:
|
| @@ -473,9 +521,14 @@ class LinkerRandomizationTest(LinkerTestCaseBase):
|
| renderer_status, renderer_logs = _CheckLoadAddressRandomization(
|
| renderer_lib_map_list, 'Renderer')
|
|
|
| + browser_config = _GetBrowserSharedRelroConfig()
|
| + if not browser_config:
|
| + return ResultType.FAIL, 'Bad linker source configuration'
|
| +
|
| if not browser_status:
|
| - if self.is_low_memory:
|
| - return ResultType.FAIL, browser_logs
|
| + if browser_config == 'ALWAYS' or \
|
| + (browser_config == 'LOW_RAM_ONLY' and self.is_low_memory):
|
| + return ResultType.FAIL, browser_logs
|
|
|
| # IMPORTANT NOTE: The system's ASLR implementation seems to be very poor
|
| # when starting an activity process in a loop with "adb shell am start".
|
| @@ -501,3 +554,67 @@ class LinkerRandomizationTest(LinkerTestCaseBase):
|
| return ResultType.FAIL, renderer_logs
|
|
|
| return ResultType.PASS, logs
|
| +
|
| +
|
| +class LinkerLowMemoryThresholdTest(LinkerTestCaseBase):
|
| + """This test checks that the definitions for the low-memory device physical
|
| + RAM threshold are identical in the base/ and linker sources. Because these
|
| + two components should absolutely not depend on each other, it's difficult
|
| + to perform this check correctly at runtime inside the linker test binary
|
| + without introducing hairy dependency issues in the build, or complicated
|
| + plumbing at runtime.
|
| +
|
| + To work-around this, this test looks directly into the sources for a
|
| + definition of the same constant that should look like:
|
| +
|
| + #define ANDROID_LOW_MEMORY_DEVICE_THRESHOLD_MB <number>
|
| +
|
| + And will check that the values for <number> are identical in all of
|
| + them."""
|
| +
|
| + # A regular expression used to find the definition of the threshold in all
|
| + # sources:
|
| + _RE_THRESHOLD_DEFINITION = re.compile(
|
| + r'^\s*#\s*define\s+ANDROID_LOW_MEMORY_DEVICE_THRESHOLD_MB\s+(\d+)\s*$',
|
| + re.MULTILINE)
|
| +
|
| + # The list of source files, relative to DIR_SOURCE_ROOT, which must contain
|
| + # a line that matches the re above.
|
| + _SOURCES_LIST = [
|
| + 'base/android/sys_utils.cc',
|
| + 'content/common/android/linker/linker_jni.cc' ]
|
| +
|
| + def _RunTest(self, adb):
|
| + failure = False
|
| + values = []
|
| + # First, collect all the values in all input sources.
|
| + re = LinkerLowMemoryThresholdTest._RE_THRESHOLD_DEFINITION
|
| + for source in LinkerLowMemoryThresholdTest._SOURCES_LIST:
|
| + source_path = os.path.join(constants.DIR_SOURCE_ROOT, source);
|
| + if not os.path.exists(source_path):
|
| + logging.error('Missing source file: ' + source_path)
|
| + failure = True
|
| + continue
|
| + with open(source_path) as f:
|
| + source_text = f.read()
|
| + # For some reason, re.match() never works here.
|
| + source_values = re.findall(source_text)
|
| + if not source_values:
|
| + logging.error('Missing low-memory threshold definition in ' + \
|
| + source_path)
|
| + logging.error('Source:\n%s\n' % source_text)
|
| + failure = True
|
| + continue
|
| + values += source_values
|
| +
|
| + # Second, check that they are all the same.
|
| + if not failure:
|
| + for value in values[1:]:
|
| + if value != values[0]:
|
| + logging.error('Value mismatch: ' + repr(values))
|
| + failure = True
|
| +
|
| + if failure:
|
| + return ResultType.FAIL, 'Incorrect low-end memory threshold definitions!'
|
| +
|
| + return ResultType.PASS, ''
|
|
|