0

android: Remove the linker tests.

The linker tests don't run on bots (and likely not locally either) and
do not support the latest library loading patterns. This is the first
step to remove them, as they add non-trivial complexity in the codebase.

More specifically, this removes the build rules and the test APK.

Bug: 1059707
Change-Id: I12102d59116ad1f0a152d3110cf3346471c8f390
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2093606
Reviewed-by: Andrew Grieve <agrieve@chromium.org>
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: Egor Pasko <pasko@chromium.org>
Commit-Queue: Benoit L <lizeb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#749238}
This commit is contained in:
Benoît Lizé
2020-03-11 17:55:34 +00:00
committed by Commit Bot
parent 0054df3c75
commit d023c38e9f
19 changed files with 0 additions and 956 deletions

@ -389,10 +389,6 @@ group("gn_all") {
]
}
if (target_cpu != "x64") {
deps += [ "//content/shell/android:chromium_linker_test_apk" ]
}
if (enable_chrome_android_internal) {
deps += [ "//clank" ]
}

@ -5,7 +5,6 @@
from pylib.gtest import gtest_test_instance
from pylib.instrumentation import instrumentation_test_instance
from pylib.junit import junit_test_instance
from pylib.linker import linker_test_instance
from pylib.monkey import monkey_test_instance
from pylib.utils import device_dependencies
@ -20,8 +19,6 @@ def CreateTestInstance(args, error_func):
args, device_dependencies.GetDataDependencies, error_func)
elif args.command == 'junit':
return junit_test_instance.JunitTestInstance(args, error_func)
elif args.command == 'linker':
return linker_test_instance.LinkerTestInstance(args)
elif args.command == 'monkey':
return monkey_test_instance.MonkeyTestInstance(args, error_func)

@ -5,12 +5,10 @@
from pylib.gtest import gtest_test_instance
from pylib.instrumentation import instrumentation_test_instance
from pylib.junit import junit_test_instance
from pylib.linker import linker_test_instance
from pylib.monkey import monkey_test_instance
from pylib.local.device import local_device_environment
from pylib.local.device import local_device_gtest_run
from pylib.local.device import local_device_instrumentation_test_run
from pylib.local.device import local_device_linker_test_run
from pylib.local.device import local_device_monkey_test_run
from pylib.local.machine import local_machine_environment
from pylib.local.machine import local_machine_junit_test_run
@ -24,9 +22,6 @@ def CreateTestRun(env, test_instance, error_func):
instrumentation_test_instance.InstrumentationTestInstance):
return (local_device_instrumentation_test_run
.LocalDeviceInstrumentationTestRun(env, test_instance))
if isinstance(test_instance, linker_test_instance.LinkerTestInstance):
return (local_device_linker_test_run
.LocalDeviceLinkerTestRun(env, test_instance))
if isinstance(test_instance, monkey_test_instance.MonkeyTestInstance):
return (local_device_monkey_test_run
.LocalDeviceMonkeyTestRun(env, test_instance))

@ -1,3 +0,0 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

@ -1,48 +0,0 @@
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from pylib.base import test_instance
from pylib.constants import host_paths
from pylib.linker import test_case
from pylib.utils import test_filter
with host_paths.SysPath(host_paths.BUILD_COMMON_PATH):
import unittest_util
class LinkerTestInstance(test_instance.TestInstance):
def __init__(self, args):
super(LinkerTestInstance, self).__init__()
self._test_apk = args.test_apk
self._test_filter = test_filter.InitializeFilterFromArgs(args)
@property
def test_apk(self):
return self._test_apk
@property
def test_filter(self):
return self._test_filter
def GetTests(self):
tests = [test_case.LinkerSharedRelroTest()]
if self._test_filter:
filtered_names = unittest_util.FilterTestNames(
(t.qualified_name for t in tests), self._test_filter)
tests = [
t for t in tests
if t.qualified_name in filtered_names]
return tests
def SetUp(self):
pass
def TearDown(self):
pass
def TestType(self):
return 'linker'

@ -1,199 +0,0 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Base class for linker-specific test cases.
The custom dynamic linker can only be tested through a custom test case
for various technical reasons:
- It's an 'invisible feature', i.e. it doesn't expose a new API or
behaviour, all it does is save RAM when loading native libraries.
- Checking that it works correctly requires several things that do not
fit the existing GTest-based and instrumentation-based tests:
- Native test code needs to be run in both the browser and renderer
process at the same time just after loading native libraries, in
a completely asynchronous way.
- Each test case requires restarting a whole new application process
with a different command-line.
- Enabling test support in the Linker code requires building a special
APK with a flag to activate special test-only support code in the
Linker code itself.
Host-driven tests have also been tried, but since they're really
sub-classes of instrumentation tests, they didn't work well either.
To build and run, refer to android_linker_testing.md.
"""
# pylint: disable=R0201
from __future__ import print_function
import logging
import re
from devil.android import device_errors
from devil.android.sdk import intent
from pylib.base import base_test_result
ResultType = base_test_result.ResultType
_PACKAGE_NAME = 'org.chromium.chromium_linker_test_apk'
_ACTIVITY_NAME = '.ChromiumLinkerTestActivity'
# 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 others as well when troubleshooting.
_LOGCAT_FILTERS = ['*:s', 'chromium:v', 'cr_chromium:v',
'cr_ChromiumAndroidLinker:v', 'cr_LibraryLoader:v',
'cr_LinkerTest:v']
#_LOGCAT_FILTERS = ['*:v'] ## DEBUG
# Regular expression used to match status lines in logcat.
_RE_BROWSER_STATUS_LINE = re.compile(r' BROWSER_LINKER_TEST: (FAIL|SUCCESS)$')
_RE_RENDERER_STATUS_LINE = re.compile(r' RENDERER_LINKER_TEST: (FAIL|SUCCESS)$')
def _StartActivityAndWaitForLinkerTestStatus(device, timeout):
"""Force-start an activity and wait up to |timeout| seconds until the full
linker test status lines appear in the logcat, recorded through |device|.
Args:
device: A DeviceUtils instance.
timeout: Timeout in seconds
Returns:
A (status, logs) tuple, where status is a ResultType constant, and logs
if the final logcat output as a string.
"""
# 1. Start recording logcat with appropriate filters.
with device.GetLogcatMonitor(filter_specs=_LOGCAT_FILTERS) as logmon:
# 2. Force-start activity.
device.StartActivity(
intent.Intent(package=_PACKAGE_NAME, activity=_ACTIVITY_NAME),
force_stop=True)
# 3. Wait up to |timeout| seconds until the test status is in the logcat.
result = ResultType.PASS
try:
browser_match = logmon.WaitFor(_RE_BROWSER_STATUS_LINE, timeout=timeout)
logging.debug('Found browser match: %s', browser_match.group(0))
renderer_match = logmon.WaitFor(_RE_RENDERER_STATUS_LINE,
timeout=timeout)
logging.debug('Found renderer match: %s', renderer_match.group(0))
if (browser_match.group(1) != 'SUCCESS'
or renderer_match.group(1) != 'SUCCESS'):
result = ResultType.FAIL
except device_errors.CommandTimeoutError:
result = ResultType.TIMEOUT
logcat = device.adb.Logcat(dump=True)
logmon.Close()
return result, '\n'.join(logcat)
class LibraryLoadMap(dict):
"""A helper class to pretty-print a map of library names to load addresses."""
def __str__(self):
items = ['\'%s\': 0x%x' % (name, address) for \
(name, address) in self.iteritems()]
return '{%s}' % (', '.join(items))
def __repr__(self):
return 'LibraryLoadMap(%s)' % self.__str__()
class AddressList(list):
"""A helper class to pretty-print a list of load addresses."""
def __str__(self):
items = ['0x%x' % address for address in self]
return '[%s]' % (', '.join(items))
def __repr__(self):
return 'AddressList(%s)' % self.__str__()
class LinkerTestCaseBase(object):
"""Base class for linker test cases."""
def __init__(self):
"""Creates a test case."""
test_suffix = 'ForLegacyLinker'
class_name = self.__class__.__name__
self.qualified_name = '%s.%s' % (class_name, test_suffix)
self.tagged_name = self.qualified_name
def _RunTest(self, _device):
"""Runs the test, must be overridden.
Args:
_device: A DeviceUtils interface.
Returns:
A (status, log) tuple, where <status> is a ResultType constant, and <log>
is the logcat output captured during the test in case of error, or None
in case of success.
"""
return ResultType.FAIL, 'Unimplemented _RunTest() method!'
def Run(self, device):
"""Runs the test on a given device.
Args:
device: Name of target device where to run the test.
Returns:
A base_test_result.TestRunResult() instance.
"""
margin = 8
print('[ %-*s ] %s' % (margin, 'RUN', self.tagged_name))
logging.info('Running linker test: %s', self.tagged_name)
# Run the test.
status, logs = self._RunTest(device)
result_text = 'OK'
if status == ResultType.FAIL:
result_text = 'FAILED'
elif status == ResultType.TIMEOUT:
result_text = 'TIMEOUT'
print('[ %*s ] %s' % (margin, result_text, self.tagged_name))
return base_test_result.BaseTestResult(self.tagged_name, status, log=logs)
def __str__(self):
return self.tagged_name
def __repr__(self):
return self.tagged_name
class LinkerSharedRelroTest(LinkerTestCaseBase):
"""A linker test case to check the status of shared RELRO sections.
The core of the checks performed here are pretty simple:
- Clear the logcat and start recording with an appropriate set of filters.
- Create the command-line appropriate for the test-case.
- Start the activity (always forcing a cold start).
- Every second, look at the current content of the filtered logcat lines
and look for instances of the following:
BROWSER_LINKER_TEST: <status>
RENDERER_LINKER_TEST: <status>
where <status> can be either FAIL or SUCCESS. These lines can appear
in any order in the logcat. Once both browser and renderer status are
found, stop the loop. Otherwise timeout after 30 seconds.
Note that there can be other lines beginning with BROWSER_LINKER_TEST:
and RENDERER_LINKER_TEST:, but are not followed by a <status> code.
- The test case passes if the <status> for both the browser and renderer
process are SUCCESS. Otherwise its a fail.
"""
def _RunTest(self, device):
# Wait up to 30 seconds until the linker test status is in the logcat.
return _StartActivityAndWaitForLinkerTestStatus(device, timeout=30)

@ -1,75 +0,0 @@
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import logging
import sys
import traceback
from pylib.base import base_test_result
from pylib.linker import test_case
from pylib.local.device import local_device_environment
from pylib.local.device import local_device_test_run
class LinkerExceptionTestResult(base_test_result.BaseTestResult):
"""Test result corresponding to a python exception in a host-custom test."""
def __init__(self, test_name, exc_info):
"""Constructs a LinkerExceptionTestResult object.
Args:
test_name: name of the test which raised an exception.
exc_info: exception info, ostensibly from sys.exc_info().
"""
exc_type, exc_value, exc_traceback = exc_info
trace_info = ''.join(traceback.format_exception(exc_type, exc_value,
exc_traceback))
log_msg = 'Exception:\n' + trace_info
super(LinkerExceptionTestResult, self).__init__(
test_name,
base_test_result.ResultType.FAIL,
log="%s %s" % (exc_type, log_msg))
class LocalDeviceLinkerTestRun(local_device_test_run.LocalDeviceTestRun):
def _CreateShards(self, tests):
return tests
def _GetTests(self):
return self._test_instance.GetTests()
def _GetUniqueTestName(self, test):
return test.qualified_name
def _RunTest(self, device, test):
assert isinstance(test, test_case.LinkerTestCaseBase)
try:
result = test.Run(device)
except Exception: # pylint: disable=broad-except
logging.exception('Caught exception while trying to run test: ' +
test.tagged_name)
exc_info = sys.exc_info()
result = LinkerExceptionTestResult(test.tagged_name, exc_info)
return result, None
def SetUp(self):
@local_device_environment.handle_shard_failures_with(
on_failure=self._env.BlacklistDevice)
def individual_device_set_up(dev):
dev.Install(self._test_instance.test_apk)
self._env.parallel_devices.pMap(individual_device_set_up)
def _ShouldShard(self):
return True
def TearDown(self):
pass
def TestPackage(self):
pass

@ -160,15 +160,11 @@ pylib/instrumentation/instrumentation_test_instance.py
pylib/instrumentation/test_result.py
pylib/junit/__init__.py
pylib/junit/junit_test_instance.py
pylib/linker/__init__.py
pylib/linker/linker_test_instance.py
pylib/linker/test_case.py
pylib/local/__init__.py
pylib/local/device/__init__.py
pylib/local/device/local_device_environment.py
pylib/local/device/local_device_gtest_run.py
pylib/local/device/local_device_instrumentation_test_run.py
pylib/local/device/local_device_linker_test_run.py
pylib/local/device/local_device_monkey_test_run.py
pylib/local/device/local_device_test_run.py
pylib/local/emulator/__init__.py

@ -245,106 +245,6 @@ android_library("content_shell_test_java") {
]
}
if (current_cpu != "x64") {
chromium_linker_test_manifest =
"$target_gen_dir/linker_test_apk/AndroidManifest.xml"
jinja_template("chromium_linker_test_manifest") {
testonly = true
input = "linker_test_apk/AndroidManifest.xml.jinja2"
output = chromium_linker_test_manifest
}
android_resources("linker_resources") {
testonly = true
resource_dirs = [ "linker_test_apk/res" ]
android_manifest = chromium_linker_test_manifest
android_manifest_dep = ":chromium_linker_test_manifest"
}
_linker_test_apk_target_name = "chromium_linker_test_apk__apk"
_linker_test_apk_test_runner_target_name =
"chromium_linker_test_apk__test_runner_script"
_linker_test_jni_registration_header =
"$target_gen_dir/linker_test_apk/linker_test_jni_registration.h"
android_apk(_linker_test_apk_target_name) {
testonly = true
deps = [
":content_shell_assets",
":content_shell_java",
":linker_resources",
"//base:base_java",
"//base:jni_java",
"//content/public/android:content_java",
"//ui/android:ui_java",
]
android_manifest = chromium_linker_test_manifest
android_manifest_dep = ":chromium_linker_test_manifest"
apk_name = "ChromiumLinkerTest"
shared_libraries = [ ":linker_test" ]
use_chromium_linker = true
enable_chromium_linker_tests = true
jni_registration_header = _linker_test_jni_registration_header
sources = [
"linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestActivity.java",
"linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestApplication.java",
"linker_test_apk/src/org/chromium/chromium_linker_test_apk/LinkerTests.java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
processor_args_javac = [ skip_gen_jni_arg ]
}
test_runner_script(_linker_test_apk_test_runner_target_name) {
test_name = "chromium_linker_test_apk"
test_type = "linker"
apk_target = ":$_linker_test_apk_target_name"
ignore_all_data_deps = true
}
group("chromium_linker_test_apk") {
testonly = true
deps = [
":$_linker_test_apk_target_name",
":$_linker_test_apk_test_runner_target_name",
]
}
shared_library("linker_test") {
testonly = true
sources = [
"linker_test_apk/chromium_linker_test_android.cc",
"linker_test_apk/chromium_linker_test_linker_tests.cc",
_linker_test_jni_registration_header,
]
deps = [
":${_linker_test_apk_target_name}__final_jni",
":linker_test_jni_headers",
"//content/shell:content_shell_lib",
# Required to include "content/public/browser/android/compositor.h"
# in chromium_linker_test_android.cc :-(
"//skia",
"//third_party/re2",
]
# Explicit dependency required for JNI registration to be able to
# find the native side functions.
if (is_component_build) {
deps += [
"//device/gamepad",
"//media/midi",
]
}
}
generate_jni("linker_test_jni_headers") {
testonly = true
sources = [ "linker_test_apk/src/org/chromium/chromium_linker_test_apk/LinkerTests.java" ]
}
}
android_library("content_shell_browsertests_java") {
testonly = true
deps = [

@ -1,59 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2013 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.chromium.chromium_linker_test_apk">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application android:name="ChromiumLinkerTestApplication"
android:label="ChromiumLinkerTest">
<activity android:name="ChromiumLinkerTestActivity"
android:launchMode="singleTask"
android:theme="@android:style/Theme.Holo.Light.NoActionBar"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize"
android:hardwareAccelerated="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- The following service entries exist in order to allow us to
start more than one sandboxed process. -->
<!-- NOTE: If you change the values of "android:process" for any of the below services,
you also need to update kHelperProcessExecutableName in chrome_constants.cc. -->
{% set num_sandboxed_services = 40 %}
<meta-data android:name="org.chromium.content.browser.NUM_SANDBOXED_SERVICES"
android:value="{{ num_sandboxed_services }}"/>
{% for i in range(num_sandboxed_services) %}
<service android:name="org.chromium.content.app.SandboxedProcessService{{ i }}"
android:process=":sandboxed_process{{ i }}"
android:isolatedProcess="true"
android:exported="false" />
{% endfor %}
{% set num_privileged_services = 5 %}
<meta-data android:name="org.chromium.content.browser.NUM_PRIVILEGED_SERVICES"
android:value="{{ num_privileged_services }}"/>
{% for i in range(num_privileged_services) %}
<service android:name="org.chromium.content.app.PrivilegedProcessService{{ i }}"
android:process=":privileged_process{{ i }}"
android:isolatedProcess="false"
android:exported="false" />
{% endfor %}
</application>
</manifest>

@ -1,23 +0,0 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/android/jni_android.h"
#include "base/android/library_loader/library_loader_hooks.h"
#include "base/bind.h"
#include "content/public/app/content_jni_onload.h"
#include "content/public/app/content_main.h"
#include "content/shell/android/linker_test_apk/linker_test_jni_registration.h"
#include "content/shell/app/shell_main_delegate.h"
// This is called by the VM when the shared library is first loaded.
JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
base::android::InitVM(vm);
JNIEnv* env = base::android::AttachCurrentThread();
if (!RegisterMainDexNatives(env) || !RegisterNonMainDexNatives(env) ||
!content::android::OnJNIOnLoadInit()) {
return -1;
}
content::SetContentMainDelegate(new content::ShellMainDelegate());
return JNI_VERSION_1_4;
}

@ -1,195 +0,0 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file implements the native methods of
// org.content.chromium.app.LinkerTests
// Unlike the content of linker_jni.cc, it is part of the content library and
// can thus use base/ and the C++ STL.
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/mman.h>
#include <string>
#include <vector>
#include "base/debug/proc_maps_linux.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "content/shell/android/linker_test_jni_headers/LinkerTests_jni.h"
#include "third_party/re2/src/re2/re2.h"
using base::android::JavaParamRef;
namespace content {
namespace {
using base::debug::MappedMemoryRegion;
jboolean RunChecks(bool in_browser_process) {
// IMPORTANT NOTE: The Python test control script reads the logcat for
// lines like:
// BROWSER_LINKER_TEST: <status>
// RENDERER_LINKER_TEST: <status>
//
// Where <status> can be either SUCCESS or FAIL. Other lines starting
// with the same prefixes, but not using SUCCESS or FAIL are ignored.
const char* prefix =
in_browser_process ? "BROWSER_LINKER_TEST: " : "RENDERER_LINKER_TEST: ";
// The RELRO section(s) will appear in /proc/self/maps as a mapped memory
// region for a file with a recognizable name. For the LegacyLinker the
// full name will be something like:
//
// "/dev/ashmem/RELRO:<libname> (deleted)"
//
// and for the ModernLinker, something like:
//
// "/data/data/org.chromium.chromium_linker_test_apk/
// app_chromium_linker_test/RELRO:<libname> (deleted)"
//
// Where <libname> is the library name and '(deleted)' is actually
// added by the kernel to indicate there is no corresponding file
// on the filesystem.
//
// For regular builds, there is only one library, and thus one RELRO
// section, but for the component build, there are several libraries,
// each one with its own RELRO.
static const char kLegacyRelroSectionPattern[] = "/dev/ashmem/RELRO:.*";
static const char kModernRelroSectionPattern[] = "/data/.*/RELRO:.*";
// Parse /proc/self/maps and builds a list of region mappings in this
// process.
std::string maps;
base::debug::ReadProcMaps(&maps);
if (maps.empty()) {
LOG(ERROR) << prefix << "FAIL Cannot parse /proc/self/maps";
return false;
}
std::vector<MappedMemoryRegion> regions;
base::debug::ParseProcMaps(maps, &regions);
if (regions.empty()) {
LOG(ERROR) << prefix << "FAIL Cannot read memory mappings in this process";
return false;
}
const RE2 legacy_linker_re(kLegacyRelroSectionPattern);
const RE2 modern_linker_re(kModernRelroSectionPattern);
int num_shared_relros = 0;
int num_bad_shared_relros = 0;
for (size_t n = 0; n < regions.size(); ++n) {
MappedMemoryRegion& region = regions[n];
const std::string path = region.path;
const bool is_legacy_relro = re2::RE2::FullMatch(path, legacy_linker_re);
const bool is_modern_relro = re2::RE2::FullMatch(path, modern_linker_re);
if (is_legacy_relro && is_modern_relro) {
LOG(ERROR) << prefix
<< "FAIL RELRO cannot be both Legacy and Modern (test error)";
return false;
}
if (!is_legacy_relro && !is_modern_relro) {
// Ignore any mapping that isn't a shared RELRO.
continue;
}
num_shared_relros++;
void* region_start = reinterpret_cast<void*>(region.start);
void* region_end = reinterpret_cast<void*>(region.end);
// Check that it is mapped read-only.
const uint8_t expected_flags = MappedMemoryRegion::READ;
const uint8_t expected_mask = MappedMemoryRegion::READ |
MappedMemoryRegion::WRITE |
MappedMemoryRegion::EXECUTE;
uint8_t region_flags = region.permissions & expected_mask;
if (region_flags != expected_flags) {
LOG(ERROR)
<< prefix
<< base::StringPrintf(
"Shared RELRO section at %p-%p is not mapped read-only. "
"Protection flags are %d (%d expected)!",
region_start,
region_end,
region_flags,
expected_flags);
num_bad_shared_relros++;
continue;
}
// Shared RELROs implemented by ModernLinker are not in ashmem. ModernLinker
// (via android_dlopen_ext()) maps everything with MAP_PRIVATE rather than
// MAP_SHARED. Remapping such a RELRO section read-write will therefore
// succeed, but it is not a problem. The memory copy-on-writes, and updates
// are not visible to either the mapped file or other processes mapping the
// same file. So... we skip the remap test for ModernLinker.
if (is_modern_relro) {
continue;
}
// Check that trying to remap it read-write fails with EACCES
size_t region_size = region.end - region.start;
int ret = ::mprotect(region_start, region_size, PROT_READ | PROT_WRITE);
if (ret != -1) {
LOG(ERROR)
<< prefix
<< base::StringPrintf(
"Shared RELRO section at %p-%p could be remapped read-write!?",
region_start,
region_end);
num_bad_shared_relros++;
// Just in case.
::mprotect(region_start, region_size, PROT_READ);
} else if (errno != EACCES) {
LOG(ERROR) << prefix << base::StringPrintf(
"Shared RELRO section at %p-%p failed "
"read-write mprotect with "
"unexpected error %d (EACCES:%d wanted): %s",
region_start,
region_end,
errno,
EACCES,
strerror(errno));
num_bad_shared_relros++;
}
}
VLOG(0) << prefix
<< base::StringPrintf(
"There are %d shared RELRO sections in this process, of which "
"%d are bad",
num_shared_relros, num_bad_shared_relros);
if (num_bad_shared_relros > 0) {
LOG(ERROR) << prefix << "FAIL Bad RELROs sections in this process";
return false;
}
if (num_shared_relros == 0) {
LOG(ERROR) << prefix
<< "FAIL Missing shared RELRO sections in this process!";
return false;
}
VLOG(0) << prefix << "SUCCESS";
return true;
}
} // namespace
jboolean JNI_LinkerTests_CheckForSharedRelros(JNIEnv* env,
jboolean in_browser_process) {
return RunChecks(in_browser_process);
}
} // namespace content

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2013 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical">
<org.chromium.content_shell.ShellManager
android:id="@+id/shell_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

@ -1,122 +0,0 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chromium_linker_test_apk;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import org.chromium.base.Log;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.library_loader.LibraryProcessType;
import org.chromium.base.library_loader.Linker;
import org.chromium.content_public.browser.BrowserStartupController;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_shell.Shell;
import org.chromium.content_shell.ShellManager;
import org.chromium.ui.base.ActivityWindowAndroid;
/**
* Test activity used for verifying the different configuration options for the ContentLinker.
*/
public class ChromiumLinkerTestActivity extends Activity {
private static final String TAG = "LinkerTest";
private ShellManager mShellManager;
private ActivityWindowAndroid mWindowAndroid;
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Setup the TestRunner class name.
Linker.setupForTesting(Linker.LINKER_IMPLEMENTATION_LEGACY,
"org.chromium.chromium_linker_test_apk.LinkerTests");
// Load the library in the browser process, this will also run the test
// runner in this process.
LibraryLoader.getInstance().ensureInitialized();
// Now, start a new renderer process by creating a new view.
// This will run the test runner in the renderer process.
LayoutInflater inflater =
(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.test_activity, null);
mShellManager = view.findViewById(R.id.shell_container);
mWindowAndroid = new ActivityWindowAndroid(this, false);
mShellManager.setWindow(mWindowAndroid);
mShellManager.setStartupUrl("about:blank");
BrowserStartupController.getInstance().startBrowserProcessesAsync(
LibraryProcessType.PROCESS_BROWSER, true, false,
new BrowserStartupController.StartupCallback() {
@Override
public void onSuccess() {
finishInitialization(savedInstanceState);
}
@Override
public void onFailure() {
initializationFailed();
}
});
// TODO(digit): Ensure that after the content view is initialized,
// the program finishes().
}
private void finishInitialization(Bundle savedInstanceState) {
String shellUrl = ShellManager.DEFAULT_SHELL_URL;
mShellManager.launchShell(shellUrl);
}
private void initializationFailed() {
Log.e(TAG, "ContentView initialization failed.");
finish();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mWindowAndroid.saveInstanceState(outState);
}
@Override
protected void onStop() {
super.onStop();
WebContents webContents = getActiveWebContents();
if (webContents != null) webContents.onHide();
}
@Override
protected void onStart() {
super.onStart();
WebContents webContents = getActiveWebContents();
if (webContents != null) webContents.onHide();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mWindowAndroid.onActivityResult(requestCode, resultCode, data);
}
/**
* @return The {@link WebContents} owned by the currently visible {@link Shell} or null if
* one is not showing.
*/
public WebContents getActiveWebContents() {
if (mShellManager == null) return null;
Shell shell = mShellManager.getActiveShell();
return shell != null ? shell.getWebContents() : null;
}
}

@ -1,43 +0,0 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chromium_linker_test_apk;
import android.app.Application;
import android.content.Context;
import org.chromium.base.BuildConfig;
import org.chromium.base.ContextUtils;
import org.chromium.base.PathUtils;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.library_loader.LibraryProcessType;
import org.chromium.base.multidex.ChromiumMultiDexInstaller;
import org.chromium.ui.base.ResourceBundle;
/**
* Application for testing the Chromium Linker
*/
public class ChromiumLinkerTestApplication extends Application {
private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chromium_linker_test";
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
boolean isBrowserProcess = !ContextUtils.getProcessName().contains(":");
LibraryLoader.getInstance().setLibraryProcessType(isBrowserProcess
? LibraryProcessType.PROCESS_BROWSER
: LibraryProcessType.PROCESS_CHILD);
if (BuildConfig.IS_MULTIDEX_ENABLED) {
ChromiumMultiDexInstaller.install(this);
}
ContextUtils.initApplicationContext(this);
ResourceBundle.setNoAvailableLocalePaks();
}
@Override
public void onCreate() {
super.onCreate();
PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
}
}

@ -1,32 +0,0 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chromium_linker_test_apk;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.base.library_loader.Linker;
/**
* A class that is only used in linker test APK to perform runtime checks
* in the current process.
*/
@JNINamespace("content")
public class LinkerTests implements Linker.TestRunner {
private static final String TAG = "LinkerTest";
public LinkerTests() {}
@Override
public boolean runChecks(boolean isBrowserProcess) {
return LinkerTestsJni.get().checkForSharedRelros(isBrowserProcess);
}
@NativeMethods
interface Natives {
// Check that there are shared RELRO sections in the current process,
// and that they are properly mapped read-only. Returns true on success.
boolean checkForSharedRelros(boolean isBrowserProcess);
}
}

@ -104,7 +104,6 @@ display log messages to the `LogCat` pane.
<classpathentry kind="src" path="content/shell/android/java/src"/>
<classpathentry kind="src" path="content/shell/android/shell_apk/src"/>
<classpathentry kind="src" path="content/shell/android/javatests/src"/>
<classpathentry kind="src" path="content/shell/android/linker_test_apk/src"/>
<classpathentry kind="lib" path="third_party/android_sdk/public/platforms/android-27/data/layoutlib.jar"/>
<classpathentry kind="lib" path="third_party/android_sdk/public/platforms/android-27/android.jar"/>
<classpathentry kind="output" path="out/bin"/>

@ -4,13 +4,6 @@ The crazy linker is a custom dynamic linker used by Chrome on older Android
versions where dynamic linking is not as advanced. It provides
`android_dlopen_ext` functionality, RELRO sharing, compressed relocations, etc.
For crazy reasons outlined in `linker/test_case.py` this linker cannot be tested
using GTest or instrumentation test, hence it also carries a custom testing
framework. The tests are not run as part of CQ, but it is still desirable to run
them before landing changes in code locations listed below.
Sorry.
These instructions assume
[Building Chromium for Android](android_build_instructions.md) as a
prerequisite.
@ -23,12 +16,6 @@ third_party/android_crazy_linker
base/android/java/src/org/chromium/base/library_loader
```
The tests themselves are living mostly in these places:
```
build/android/pylib/linker/test_case.py
content/shell/android
```
## Running native tests
This will run both unittests and regression tests:
@ -40,16 +27,6 @@ out/Release/bin/run_android_crazy_linker_tests --unit-tests
Verbosity of the output can be increased by setting `CRAZY_DEBUG` to 1 in
`crazy_linker_debug.h`.
## Running Java Tests
We recommend running these tests in Release mode, as there are known
complications in testing with the component build. Setting `Linker.DEBUG` to
`true` should also help increase verbosity of the output.
```
autoninja -C out/Release chromium_linker_test_apk
out/Release/bin/run_chromium_linker_test_apk
```
## Fuzzer Tests
There are also a few tests for fuzzing the ZIP parser. The instructions to run

@ -220,7 +220,6 @@ to the classpath for downstream development. See "additional_entries" below.
<classpathentry kind="src" path="content/shell/android/browsertests_apk/src"/>
<classpathentry kind="src" path="content/shell/android/java/src"/>
<classpathentry kind="src" path="content/shell/android/javatests/src"/>
<classpathentry kind="src" path="content/shell/android/linker_test_apk/src"/>
<classpathentry kind="src" path="content/shell/android/shell_apk/src"/>
<classpathentry kind="src" path="device/bluetooth/android/java/src"/>
<classpathentry kind="src" path="device/bluetooth/test/android/java/src"/>