Commit aa6a5ca9 authored by Egor Pasko's avatar Egor Pasko Committed by Chromium LUCI CQ

base/android: Make Linker an implementation detail of LibraryLoader

Restrict Linker functionality to be "package private", do not expose
the whole Linker API to clients.

Reduce verbosity in code comments about the Linker, and reformulate them
in terms of RELRO producer/consumer, rather than process type. This way
the App Zygote can become the RELRO producer without contradicting all
these explanations.

Remove ChromiumLinkerParams. Previously I thought that in the new
protocol it would expand, but now I realized that it is better to extend
the messages that happen later in Linker lifetime.

Extract the bundle exchange between linkers in different processes into
LibraryLoader's inner class. This looks cleaner because it makes the
communication protocol handled in one compact place. Before this change
it was nontrivial to verify that communication is not racy.

The plan is to add functionality to the MultiProcessMessageHandler to
make it possible to sometimes move the RELRO FD from isolated processes
into the browser process, without leaking a lot of knowledge about it
outside of the package o.c.b.library_loader. With this change the
MultiProcessMessageHandler will make sense to move to another file.

Bug: 1154224
Change-Id: I2c027961530947a93bca44ce70b8b9dcd25e1b2f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2607931
Commit-Queue: Egor Pasko <pasko@chromium.org>
Reviewed-by: default avatarBo <boliu@chromium.org>
Reviewed-by: default avatarBenoit L <lizeb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#840993}
parent 53b1beb8
......@@ -37,7 +37,7 @@ class LegacyLinker extends Linker {
ensureInitializedLocked();
assert mState == State.INITIALIZED; // Only one successful call.
boolean provideRelro = mInBrowserProcess;
boolean produceRelro = mRelroProducer;
long loadAddress = isFixedAddressPermitted ? mBaseLoadAddress : 0;
String libFilePath = System.mapLibraryName(library);
......@@ -50,7 +50,7 @@ class LegacyLinker extends Linker {
}
libInfo.mLibFilePath = libFilePath;
if (provideRelro) {
if (produceRelro) {
if (!nativeCreateSharedRelro(sharedRelRoName, mBaseLoadAddress, libInfo)) {
Log.w(TAG, "Could not create shared RELRO for %s at %x", libFilePath,
mBaseLoadAddress);
......@@ -67,6 +67,7 @@ class LegacyLinker extends Linker {
useSharedRelrosLocked(mLibInfo);
mState = State.DONE_PROVIDE_RELRO;
} else {
// Consume RELRO.
waitForSharedRelrosLocked();
assert libFilePath.equals(mLibInfo.mLibFilePath);
useSharedRelrosLocked(mLibInfo);
......
......@@ -10,6 +10,7 @@ import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.SystemClock;
import android.system.Os;
......@@ -40,6 +41,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Locale;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
/**
* This class provides functionality to load and register the native libraries.
......@@ -101,7 +103,7 @@ public class LibraryLoader {
// Avoids locking: should be initialized very early.
private boolean mConfigurationSet;
// The type of process the shared library is loaded in.
// The type of process the shared library is loaded in. Gets passed to native after loading.
// Avoids locking: should be initialized very early.
private @LibraryProcessType int mLibraryProcessType;
......@@ -109,6 +111,9 @@ public class LibraryLoader {
// except the volatile |mLoadState|.
private final Object mNonMainDexLock = new Object();
// Mediates all communication between Linker instances in different processes.
private final MultiProcessMediator mMessageHandler = new MultiProcessMediator();
// Guards all the fields below.
private final Object mLock = new Object();
......@@ -128,11 +133,118 @@ public class LibraryLoader {
@GuardedBy("mLock")
private boolean mCommandLineSwitched;
// The number of milliseconds it took to load all the native libraries, which
// will be reported via UMA. Set once when the libraries are done loading.
// The number of milliseconds it took to load all the native libraries, which will be reported
// via UMA. Set once when the libraries are done loading.
@GuardedBy("mLock")
private long mLibraryLoadTimeMs;
/**
* Inner class encapsulating points of communication between instances of LibraryLoader in
* different processes.
*
* Usage:
*
* - For a {@link LibraryLoader} requiring the knowledge of the load address before
* initialization, {@link #takeLoadAddressFromBundle(Bundle)} should be called first. It is
* done very early after establishing a Binder connection.
*
* - To initialize the object, one of {@link #ensureInitializedInMainProcess()} and
* {@link #initInChildProcess()} must be called. Subsequent calls to initialization are
* ignored.
*
* - Later {@link #putLoadAddressToBundle(Bundle)} and
* {@link #takeLoadAddressFromBundle(Bundle)} should be called for passing the RELRO
* information between library loaders.
*
* Internally the {@LibraryLoader} may ignore these messages because it can fall back to not
* sharing RELRO.
*/
@ThreadSafe
public class MultiProcessMediator {
@GuardedBy("mLock")
private long mLoadAddress;
// Used only for asserts, and only ever switched from false to true.
private volatile boolean mInitDone;
/**
* Extracts the load address as provided by another process.
* @param bundle The Bundle to extract from.
*/
public void takeLoadAddressFromBundle(Bundle bundle) {
// Currently clients call this method strictly before any other method can get executed
// on a different thread. Hence, synchronization is not required, but verification of
// correctness is still non-trivial, and over-synchronization is cheap compared to
// library loading.
synchronized (mLock) {
mLoadAddress = Linker.extractLoadAddressFromBundle(bundle);
}
}
/**
* Initializes the Browser process side of communication, the one that coordinates creation
* of other processes. Can be called more than once, subsequent calls are ignored.
*/
public void ensureInitializedInMainProcess() {
if (mInitDone) return;
if (useChromiumLinker()) {
Linker.getInstance().initAsRelroProducer();
}
mInitDone = true;
}
/**
* Serializes the load address for communication, if any was determined during
* initialization. Must be called after the library has been loaded in this process.
* @param bundle Bundle to put the address to.
*/
public void putLoadAddressToBundle(Bundle bundle) {
assert mInitDone;
if (useChromiumLinker()) {
Linker.getInstance().putLoadAddressToBundle(bundle);
}
}
/**
* Initializes in processes other than "Main".
*/
public void initInChildProcess() {
if (useChromiumLinker()) {
synchronized (mLock) {
Linker.getInstance().initAsRelroConsumer(mLoadAddress);
}
}
mInitDone = true;
}
/**
* Optionally extracts RELRO and saves it for replacing the RELRO section in this process.
* Can be invoked before initialization.
* @param bundle Where to deserialize from.
*/
public void takeSharedRelrosFromBundle(Bundle bundle) {
if (useChromiumLinker() && !isLoadedByZygote()) {
Linker.getInstance().takeSharedRelrosFromBundle(bundle);
}
}
/**
* Optionally puts the RELRO section information so that it can be memory-mapped in another
* process reading the bundle.
* @param bundle Where to serialize.
*/
public void putSharedRelrosToBundle(Bundle bundle) {
assert mInitDone;
if (useChromiumLinker()) {
Linker.getInstance().putSharedRelrosToBundle(bundle);
}
}
}
public final MultiProcessMediator getMediator() {
return mMessageHandler;
}
/**
* Call this method to determine if the chromium project must load the library
* directly from a zip file.
......@@ -189,8 +301,9 @@ public class LibraryLoader {
* Must be called before loading the library. Since this function is called extremely early on
* in startup, locking is not required.
*
* @param useChromiumLinker Whether to use the chromium linker.
* @param useModernLinker Whether to use ModernLinker.
* @param useChromiumLinker Whether to use a chromium linker.
* @param useModernLinker Given that one of the Chromium linkers is used, whether to use
* ModernLinker instea of the LegacyLinker.
*/
public void setLinkerImplementation(boolean useChromiumLinker, boolean useModernLinker) {
assert !mInitialized;
......@@ -234,7 +347,7 @@ public class LibraryLoader {
return result;
}
public boolean useChromiumLinker() {
private boolean useChromiumLinker() {
return mUseChromiumLinker && !forceSystemLinker();
}
......@@ -329,7 +442,7 @@ public class LibraryLoader {
}
/**
* Checks if library is fully loaded and initialized.
* Checks whether the native library is fully loaded and initialized.
*/
public boolean isInitialized() {
return mInitialized && mLoadState == LoadState.LOADED;
......@@ -372,9 +485,9 @@ public class LibraryLoader {
}
/**
* Initializes the library here and now: must be called on the thread that the
* Initializes the native library: must be called on the thread that the
* native will call its "main" thread. The library must have previously been
* loaded with loadNow.
* loaded with one of the loadNow*() variants.
*/
public void initialize() {
synchronized (mLock) {
......
......@@ -26,103 +26,67 @@ import java.lang.annotation.RetentionPolicy;
import javax.annotation.concurrent.GuardedBy;
/*
* Technical note:
* This class provides a way to load the native library as an alternative to System.loadLibrary().
* It has the ability to save RAM by placing the PT_GNU_RELRO segments in a shared memory region and
* memory-mapping this region from different processes. This approach saves a few MiB RAM compared
* to the normal placement of the segment in private dirty memory.
*
* The point of this class is to provide an alternative to System.loadLibrary()
* to load the native shared library. One specific feature that it supports is the
* ability to save RAM by sharing the ELF RELRO sections between renderer
* processes.
* In the main library only one PT_GNU_RELRO segment is present, and it maps only one section
* (.data.rel.ro), so here and below it will be referred as a "RELRO section".
*
* When two processes load the same native library at the _same_ memory address,
* the content of their RELRO section (which includes C++ vtables or any
* constants that contain pointers) will be largely identical [1].
* When two processes load the same native library at the _same_ memory address, the content of
* their RELRO section (which includes C++ vtables or any constants that contain pointers) will be
* largely identical. The exceptions are pointers to external, randomized, symbols, like those from
* some system libraries, but these are very rare in practice.
*
* By default, the RELRO section is backed by private RAM in each process, which is still
* significant on mobile (e.g. ~2 MB / process on Chrome 77 ARM, more on ARM64).
* In order to establish usage of the same shared region in different processes, the Linker can
* serialize/deserialize the relevant information to/from a Bundle. Providing the RELRO shared
* memory region is done by loading the library normally, then replacing the virtual address mapping
* behind the RELRO section with the one backed by the shared memory, with identical contents.
*
* However, it is possible to save RAM by creating a shared memory region,
* copy the RELRO content into it, then have each process swap its private,
* regular RELRO, with a shared, read-only, mapping of the shared one.
*
* This trick saves 98% of the RELRO section size per extra process, after the
* first one. On the other hand, this requires careful communication between
* the process where the shared RELRO is created and the one(s) where it is used.
*
* LegacyLinker only:
* Note that swapping the regular RELRO with the shared one is not an atomic
* operation. Care must be taken that no other thread tries to run native code
* that accesses it during it. In practice, this means the swap must happen
* before library native code is executed.
*
* [1] The exceptions are pointers to external, randomized, symbols, like
* those from some system libraries, but these are very few in practice.
*/
/*
* Security considerations:
*
* - The shared RELRO memory region is always forced read-only after creation,
* which means it is impossible for a compromised service process to map
* it read-write (e.g. by calling mmap() or mprotect()) and modify its
* content, altering values seen in other service processes.
*
* - Once the RELRO ashmem region or file is mapped into a service process's
* address space, the corresponding file descriptor is immediately closed. The
* file descriptor is kept opened in the browser process, because a copy needs
* to be sent to each new potential service process.
*
* - The common library load addresses are randomized for each instance of
* the program on the device. See getRandomBaseLoadAddress() for more
* details on how this is obtained.
*/
/**
* Here's an explanation of how this class is supposed to be used:
* - The shared RELRO memory region is always forced read-only after creation, which means it is
* impossible for a compromised process to map it read-write (e.g. by calling mmap() or
* mprotect()) and modify its content, altering values seen in other processes.
*
* - The native shared library must be loaded with Linker.loadLibrary(),
* instead of System.loadLibrary(). The two functions should behave the same
* (at a high level).
* - The common library load addresses are randomized for each instance of the program on the
* device. See getRandomBaseLoadAddress() for more details on how this is obtained.
*
* - Before loading the library, setApkFilePath() must be called when loading from the APK.
* Usage:
*
* - A service process shall call initServiceProcess() early (i.e. before loadLibrary() is
* called). Otherwise, the linker considers that it is running inside the browser process. This
* is because various Chromium projects have vastly different initialization paths.
* - The native shared library must be loaded with Linker.loadLibrary(), instead of
* System.loadLibrary(). The two functions should behave the same (at a high level).
*
* - The browser is in charge of deciding where in memory each library should
* be loaded. This address must be passed to each service process (see
* {@link org.chromium.content.app.ChromiumLinkerParams} for a helper class to do so).
* - Before loading the library, setApkFilePath() must be called when loading from the APK.
*
* - The browser will also generate shared RELRO for the loaded library.
* - Early on, before the attempt to load the library, the linker needs to be initialized either as
* a provider or a consumer of the RELRO region. Depending on the choice either
* initAsRelroProducer() or initAsRelroConsumer() should be invoked. Since various Chromium
* projects have vastly different initialization paths, for convenience the initialization runs
* implicitly as part of loading the library. In this case the behaviour is of a producer.
*
* - Once the library has been loaded in the browser process, one can call getSharedRelros() which
* returns a Bundle instance containing the shared RELRO region.
* - When running as a RELRO consumer, the loadLibrary() may block until the RELRO section Bundle
* is received. This is done by calling takeSharedRelrosFromBundle() from another thread.
*
* This Bundle must be passed to each service process, for example through
* a Binder call (note that the Bundle includes file descriptors and cannot
* be added as an Intent extra).
*
* - In a service process, loadLibrary() may block until the RELRO section Bundle is received. This
* is typically done by calling provideSharedRelros() from another thread.
*
* This method also ensures the process uses the shared RELROs.
* - After loading the native library as a RELRO producer, the putSharedRelrosToBundle() becomes
* available to then send the Bundle to Linkers in other processes.
*/
@JniIgnoreNatives
public abstract class Linker {
// Log tag for this class.
abstract class Linker {
private static final String TAG = "Linker";
// Name of the library that contains our JNI code.
// Name of the library that contains the JNI code.
protected static final String LINKER_JNI_LIBRARY = "chromium_android_linker";
// Set to true to enable debug logs.
protected static final boolean DEBUG = false;
// Used to pass the shared RELRO Bundle through Binder.
public static final String EXTRA_LINKER_SHARED_RELROS =
"org.chromium.base.android.linker.shared_relros";
// Constants used to pass the shared RELRO Bundle through Binder.
private static final String SHARED_RELROS = "org.chromium.base.android.linker.shared_relros";
private static final String BASE_LOAD_ADDRESS =
"org.chromium.base.android.linker.base_load_address";
// Singleton.
protected static final Object sLock = new Object();
@GuardedBy("sLock")
......@@ -131,39 +95,43 @@ public abstract class Linker {
@GuardedBy("sLock")
protected LibInfo mLibInfo;
// Set to true if this runs in the browser process. Disabled by initServiceProcess().
// Whether this Linker instance should potentially create the RELRO region. Even if true, the
// library loading can fall back to the system linker without producing the region. The default
// value is used in tests, it is set to true so that the Linker does not have to wait for RELRO
// to arrive from another process.
@GuardedBy("sLock")
protected boolean mInBrowserProcess = true;
protected boolean mRelroProducer = true;
// Current common random base load address. A value of -1 indicates not yet initialized.
@GuardedBy("sLock")
protected long mBaseLoadAddress = -1;
/**
* States for library loading. The only transitions are forward ones.
* The state machine of library loading.
*
* The states are:
* - UNINITIALIZED: Initial state.
* - INITIALIZED: After linker initialization. Required for using the linker.
*
* When loading a library, there are several cases:
* - relro will be shared: the browser process loads the library and provides them, other
* processes wait for them to load the library (ModernLinker), or load then wait
* (LegacyLinker).
* - relro will not be shared.
* When loading a library, there are two possibilities:
*
* - RELRO is not shared.
*
* - ModernLinker: RELRO is shared: the producer process loads the library, other
* processes wait for RELRO regions to load the library
*
* - LegacyLinker: load then wait
*
* Once the library has been loaded, in the browser process the state is DONE_PROVIDE_RELRO,
* and in other processes DONE.
* Once the library has been loaded, in the producer process the state is DONE_PROVIDE_RELRO,
* and in consumer processes it is DONE.
*
* Transitions are then:
* Transitions are:
* All processes: UNINITIALIZED -> INITIALIZED
* Browser: INITIALIZED -> DONE_PROVIDE_RELRO
* Other: INITIALIZED -> DONE
* Producer: INITIALIZED -> DONE_PROVIDE_RELRO
* Consumer: INITIALIZED -> DONE
*
* When relro sharing is not happening:
* INITIALIZED -> DONE_PROVIDE_RELRO (Browser in case of relro sharing error)
* INITIALIZED -> DONE (Other processes, or browser when relro sharing was disabled, possibly
* due to a retry after a load failure.)
* When RELRO sharing failed for one reason or another, the state transitions remain the same,
* despite DONE_PROVIDE_RELRO being not appropriate as a name for this case.
*/
@IntDef({State.UNINITIALIZED, State.INITIALIZED, State.DONE_PROVIDE_RELRO, State.DONE})
@Retention(RetentionPolicy.SOURCE)
......@@ -182,14 +150,14 @@ public abstract class Linker {
protected Linker() {}
/**
* Get singleton instance. Returns either a LegacyLinker or a ModernLinker.
* Returns the singleton instance. Returns either a LegacyLinker or a ModernLinker.
*
* ModernLinker requires OS features from Android M and later: a system linker
* that handles packed relocations and load from APK, and |android_dlopen_ext()|
* for shared RELRO support. It cannot run on Android releases earlier than M.
* ModernLinker requires OS features from Android M and later: a system linker that handles
* packed relocations and load from APK, and |android_dlopen_ext()| for shared RELRO support. It
* cannot run on Android releases earlier than M.
*
* LegacyLinker runs on all Android releases but it is slower and more complex
* than ModernLinker. We still use it on M as it avoids writing the relocation to disk.
* LegacyLinker runs on all Android releases but it is slower and more complex than
* ModernLinker. The LegacyLinker is used on M as it avoids writing the relocation to disk.
*
* On N, O and P Monochrome is selected by Play Store. With Monochrome this code is not used,
* instead Chrome asks the WebView to provide the library (and the shared RELRO). If the WebView
......@@ -203,7 +171,7 @@ public abstract class Linker {
*
* @return the Linker implementation instance.
*/
public static Linker getInstance(ApplicationInfo info) {
static Linker getInstance(ApplicationInfo info) {
// A non-monochrome APK (such as ChromePublic.apk) can be installed on N+ in these
// circumstances:
// * installing APK manually
......@@ -241,12 +209,63 @@ public abstract class Linker {
* Version of getInstance() which will use the ApplicationInfo from
* ContextUtils.getApplicationContext().
*/
public static Linker getInstance() {
static Linker getInstance() {
return getInstance(ContextUtils.getApplicationContext().getApplicationInfo());
}
/**
* Obtain a random base load address at which to place loaded libraries.
* Initializes the Linker and ensures that after loading the native library the RELRO region
* will be available for sharing with other processes via
* {@link #putSharedRelrosToBundle(Bundle)}.
*/
final void initAsRelroProducer() {
synchronized (sLock) {
mRelroProducer = true;
ensureInitializedLocked();
if (DEBUG) Log.i(TAG, "initAsRelroProducer() chose address=0x%x", mBaseLoadAddress);
}
}
/**
* Initializes the Linker in the mode prepared to receive a RELRO region information from
* another process. Arrival of the RELRO region may block loading the native library in this
* process.
*
* @param baseLoadAddress the base library load address to use.
*/
final void initAsRelroConsumer(long baseLoadAddress) {
if (DEBUG) Log.i(TAG, "initAsRelroConsumer(0x%x) called", baseLoadAddress);
synchronized (sLock) {
mRelroProducer = false;
ensureInitializedLocked();
mBaseLoadAddress = baseLoadAddress;
}
}
/**
* Extracts the native library start address as serialized by
* {@link #putLoadAddressToBundle(Bundle)} in a Linker instance from another process.
*/
static long extractLoadAddressFromBundle(Bundle bundle) {
return bundle.getLong(BASE_LOAD_ADDRESS, 0);
}
/**
* Serializes the native library start address. If not asked to be initialized previously,
* initializes the Linker as a RELRO producer.
* @param bundle Bundle to put the address to.
*/
void putLoadAddressToBundle(Bundle bundle) {
synchronized (sLock) {
ensureInitializedLocked();
if (mBaseLoadAddress != 0) {
bundle.putLong(BASE_LOAD_ADDRESS, mBaseLoadAddress);
}
}
}
/**
* Obtains a random base load address at which to place loaded libraries.
*
* @return new base load address
*/
......@@ -264,8 +283,7 @@ public abstract class Linker {
//
// The above notes mean that all of this is probabilistic. It is however okay to do
// because if, worst case and unlikely, we get unlucky in our choice of address,
// the back-out and retry without the shared RELRO in the ChildProcessService will
// keep things running.
// the fallback to no RELRO sharing guarantees correctness.
final long address = nativeGetRandomBaseLoadAddress();
if (DEBUG) Log.i(TAG, "Random native base load address: 0x%x", address);
return address;
......@@ -275,7 +293,7 @@ public abstract class Linker {
void setApkFilePath(String path) {}
/**
* Load a native shared library with the Chromium linker. Note the crazy linker treats
* Loads a native shared library with the Chromium linker. Note the crazy linker treats
* libraries and files as equivalent, so you can only open one library in a given zip
* file. The library must not be the Chromium linker library.
*
......@@ -292,84 +310,49 @@ public abstract class Linker {
}
/**
* Call this to send a Bundle containing the shared RELRO section to be used in this process. If
* initServiceProcess() was previously called, loadLibrary() will not exit until this method is
* called in another thread with a non-null value.
*
* @param bundle The Bundle instance containing the shared RELRO section to use in this process.
*/
public final void provideSharedRelros(Bundle bundle) {
if (DEBUG) Log.i(TAG, "provideSharedRelros() called with %s", bundle);
synchronized (sLock) {
assert mState != State.DONE && mState != State.DONE_PROVIDE_RELRO;
// Note that in certain cases, this can be called before
// initServiceProcess() in service processes.
mLibInfo = LibInfo.fromBundle(bundle);
// Tell any listener blocked in loadLibraryImplLocked() about it.
sLock.notifyAll();
}
}
/**
* Call this to retrieve the shared RELRO sections created in this process, after loading the
* library.
*
* @return a new Bundle instance, or null if RELRO sharing is disabled via
* |isFixedAddressPermitted=false|, or if initServiceProcess() was called previously.
* Serializes information and about the RELRO region to be passed to a Linker in another
* process.
* @param bundle The Bundle to serialize to.
*/
public final Bundle getSharedRelros() {
void putSharedRelrosToBundle(Bundle bundle) {
Bundle relros = null;
synchronized (sLock) {
if (mState == State.DONE_PROVIDE_RELRO) {
assert mInBrowserProcess;
Bundle result = mLibInfo.toBundle();
if (DEBUG) Log.i(TAG, "getSharedRelros() = %s", result.toString());
return result;
assert mRelroProducer;
relros = mLibInfo.toBundle();
}
if (DEBUG) Log.i(TAG, "getSharedRelros() = null");
return null;
}
}
/**
* Call this method before loading the library to indicate that this process is ready to reuse
* shared RELRO sections from another one. Typically used when starting service processes.
*
* @param baseLoadAddress the base library load address to use.
*/
public final void initServiceProcess(long baseLoadAddress) {
if (DEBUG) Log.i(TAG, "initServiceProcess(0x%x) called", baseLoadAddress);
synchronized (sLock) {
mInBrowserProcess = false;
ensureInitializedLocked();
assert mState == State.INITIALIZED;
mBaseLoadAddress = baseLoadAddress;
}
bundle.putBundle(SHARED_RELROS, relros);
if (DEBUG) Log.i(TAG, "putSharedRelrosToBundle() puts %s", relros);
}
/**
* Retrieve the base load address of the library. This also enforces initializing the linker.
*
* @return a common, random base load address, or 0 if RELRO sharing is
* disabled.
* Deserializes the RELRO region information that was marshalled by
* {@link #putLoadAddressToBundle(Bundle)} and wakes up the threads waiting for it to use (mmap)
* replace the RELRO section in this process with shared memory.
* @param bundle The Bundle to extract the information from.
*/
public final long getBaseLoadAddress() {
synchronized (sLock) {
ensureInitializedLocked();
setupBaseLoadAddressLocked();
if (DEBUG) Log.i(TAG, "getBaseLoadAddress() returns 0x%x", mBaseLoadAddress);
return mBaseLoadAddress;
void takeSharedRelrosFromBundle(Bundle bundle) {
if (DEBUG) Log.i(TAG, "called takeSharedRelrosFromBundle(%s)", bundle);
Bundle relros = bundle.getBundle(SHARED_RELROS);
if (relros != null) {
synchronized (sLock) {
assert mState != State.DONE && mState != State.DONE_PROVIDE_RELRO;
// This can be called before initAsRelroConsumer().
mLibInfo = LibInfo.fromBundle(relros);
// Wake up blocked callers of {@link #waitForSharedRelrosLocked()}.
sLock.notifyAll();
}
}
}
/**
* Implements loading the native shared library with the Chromium linker.
*
* Load a native shared library with the Chromium linker. If the library is within a zip file
* Load a native shared library with a Chromium linker. If the library is within a zip file
* it must be uncompressed and page aligned.
*
* If asked to wait for shared RELROs, this function will block until the shared RELRO bundle
* If asked to wait for shared RELROs, this function may block until the shared RELRO bundle
* is received by provideSharedRelros().
*
* @param libFilePath The path of the library (possibly in the zip file).
......@@ -399,9 +382,11 @@ public abstract class Linker {
if (mState != State.UNINITIALIZED) return;
loadLinkerJniLibraryLocked();
// Force generation of random base load address, as well as creation of shared RELRO
// sections in this process.
if (mInBrowserProcess) setupBaseLoadAddressLocked();
if (mRelroProducer) {
assert mBaseLoadAddress == -1;
mBaseLoadAddress = getRandomBaseLoadAddress();
}
mState = State.INITIALIZED;
}
......@@ -431,12 +416,6 @@ public abstract class Linker {
}
}
// Used internally to lazily setup the common random base load address.
@GuardedBy("sLock")
private void setupBaseLoadAddressLocked() {
if (mBaseLoadAddress == -1) mBaseLoadAddress = getRandomBaseLoadAddress();
}
/**
* Record information for a given library.
*
......
......@@ -38,7 +38,7 @@ class ModernLinker extends Linker {
String libFilePath = System.mapLibraryName(library);
boolean loadNoRelro = !isFixedAddressPermitted;
boolean provideRelro = isFixedAddressPermitted && mInBrowserProcess;
boolean provideRelro = isFixedAddressPermitted && mRelroProducer;
long loadAddress = isFixedAddressPermitted ? mBaseLoadAddress : 0;
if (loadNoRelro) {
......@@ -60,7 +60,7 @@ class ModernLinker extends Linker {
libInfo.mRelroFd = -1;
}
mLibInfo = libInfo;
Log.d(TAG, "Successfully spawned RELRO: mLoadAddress=%d, mLoadSize=%d",
Log.d(TAG, "Successfully spawned RELRO: mLoadAddress=0x%x, mLoadSize=%d",
mLibInfo.mLoadAddress, mLibInfo.mLoadSize);
// Next state is still to provide relro (even if we don't have any), as child processes
// would wait for them.
......@@ -69,7 +69,7 @@ class ModernLinker extends Linker {
// Running in a child process, also with a fixed load address that is suitable for
// shared RELRO.
waitForSharedRelrosLocked();
Log.d(TAG, "Received mLibInfo: mLoadAddress=%d, mLoadSize=%d", mLibInfo.mLoadAddress,
Log.d(TAG, "Received mLibInfo: mLoadAddress=0x%x, mLoadSize=%d", mLibInfo.mLoadAddress,
mLibInfo.mLoadSize);
// Two LibInfo objects are used: |mLibInfo| that brings the RELRO FD, and a temporary
// LibInfo to load the library. Before replacing the library's RELRO with the one from
......
......@@ -137,7 +137,6 @@ android_library("content_java") {
]
sources = [
"java/src/org/chromium/content/app/ChromiumLinkerParams.java",
"java/src/org/chromium/content/app/ContentChildProcessService.java",
"java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java",
"java/src/org/chromium/content/app/ContentMain.java",
......
// 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.content.app;
import android.os.Bundle;
import java.util.Locale;
import javax.annotation.concurrent.Immutable;
/**
* A class to hold information passed from the browser process to each
* service one when using the chromium linker. For more information, read the
* technical notes in Linker.java.
*/
@Immutable
public class ChromiumLinkerParams {
// Use this base address to load native shared libraries. If 0, ignore other members.
public final long mBaseLoadAddress;
private static final String EXTRA_LINKER_PARAMS_BASE_LOAD_ADDRESS =
"org.chromium.content.common.linker_params.base_load_address";
public ChromiumLinkerParams(long baseLoadAddress) {
mBaseLoadAddress = baseLoadAddress;
}
/**
* Use this method to recreate a LinkerParams instance from a Bundle.
*
* @param bundle A Bundle, its content must have been populated by a previous
* call to populateBundle().
* @return params instance or possibly null if params was not put into bundle.
*/
public static ChromiumLinkerParams create(Bundle bundle) {
if (!bundle.containsKey(EXTRA_LINKER_PARAMS_BASE_LOAD_ADDRESS)) return null;
return new ChromiumLinkerParams(bundle);
}
private ChromiumLinkerParams(Bundle bundle) {
mBaseLoadAddress = bundle.getLong(EXTRA_LINKER_PARAMS_BASE_LOAD_ADDRESS, 0);
}
/**
* Save data in this LinkerParams instance in a bundle, to be sent to a service process.
*
* @param bundle An bundle to be passed to the child service process.
*/
public void populateBundle(Bundle bundle) {
bundle.putLong(EXTRA_LINKER_PARAMS_BASE_LOAD_ADDRESS, mBaseLoadAddress);
}
// For debugging traces only.
@Override
public String toString() {
return String.format(Locale.US, "LinkerParams(baseLoadAddress:0x%x", mBaseLoadAddress);
}
}
......@@ -20,7 +20,6 @@ import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.MainDex;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.library_loader.Linker;
import org.chromium.base.memory.MemoryPressureUma;
import org.chromium.base.process_launcher.ChildProcessServiceDelegate;
import org.chromium.base.task.PostTask;
......@@ -34,17 +33,14 @@ import org.chromium.content_public.common.ContentProcessInfo;
import java.util.List;
/**
* This implementation of {@link ChildProcessServiceDelegate} loads the native library potentially
* using the custom linker, provides access to view surfaces.
* This implementation of {@link ChildProcessServiceDelegate} loads the native library, provides
* access to view surfaces.
*/
@JNINamespace("content")
@MainDex
public class ContentChildProcessServiceDelegate implements ChildProcessServiceDelegate {
private static final String TAG = "ContentCPSDelegate";
// Linker-specific parameters for this child process service.
private ChromiumLinkerParams mLinkerParams;
private IGpuProcessCallback mGpuCallback;
private int mCpuCount;
......@@ -63,7 +59,7 @@ public class ContentChildProcessServiceDelegate implements ChildProcessServiceDe
@Override
public void onServiceBound(Intent intent) {
mLinkerParams = ChromiumLinkerParams.create(intent.getExtras());
LibraryLoader.getInstance().getMediator().takeLoadAddressFromBundle(intent.getExtras());
LibraryLoader.getInstance().setLibraryProcessType(
ChildProcessCreationParamsImpl.getLibraryProcessType(intent.getExtras()));
}
......@@ -78,11 +74,7 @@ public class ContentChildProcessServiceDelegate implements ChildProcessServiceDe
mCpuFeatures = connectionBundle.getLong(ContentChildProcessConstants.EXTRA_CPU_FEATURES);
assert mCpuCount > 0;
if (LibraryLoader.getInstance().useChromiumLinker()
&& !LibraryLoader.getInstance().isLoadedByZygote()) {
Bundle sharedRelros = connectionBundle.getBundle(Linker.EXTRA_LINKER_SHARED_RELROS);
if (sharedRelros != null) getLinker().provideSharedRelros(sharedRelros);
}
LibraryLoader.getInstance().getMediator().takeSharedRelrosFromBundle(connectionBundle);
}
@Override
......@@ -102,12 +94,10 @@ public class ContentChildProcessServiceDelegate implements ChildProcessServiceDe
JNIUtils.enableSelectiveJniRegistration();
if (LibraryLoader.getInstance().useChromiumLinker()) {
assert mLinkerParams != null;
getLinker().initServiceProcess(mLinkerParams.mBaseLoadAddress);
}
LibraryLoader.getInstance().loadNowOverrideApplicationContext(hostContext);
LibraryLoader.getInstance().registerRendererProcessHistogram();
LibraryLoader libraryLoader = LibraryLoader.getInstance();
libraryLoader.getMediator().initInChildProcess();
libraryLoader.loadNowOverrideApplicationContext(hostContext);
libraryLoader.registerRendererProcessHistogram();
initializeLibrary();
}
......@@ -140,11 +130,6 @@ public class ContentChildProcessServiceDelegate implements ChildProcessServiceDe
ContentMain.start(false);
}
// Return a Linker instance. If testing, the Linker needs special setup.
private Linker getLinker() {
return Linker.getInstance();
}
@CalledByNative
private void setFileDescriptorsIdsToKeys(int[] ids, String[] keys) {
assert ids.length == keys.length;
......
......@@ -28,14 +28,13 @@ import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.library_loader.Linker;
import org.chromium.base.library_loader.LibraryLoader.MultiProcessMediator;
import org.chromium.base.process_launcher.ChildConnectionAllocator;
import org.chromium.base.process_launcher.ChildProcessConnection;
import org.chromium.base.process_launcher.ChildProcessConstants;
import org.chromium.base.process_launcher.ChildProcessLauncher;
import org.chromium.base.process_launcher.FileDescriptorInfo;
import org.chromium.base.task.PostTask;
import org.chromium.content.app.ChromiumLinkerParams;
import org.chromium.content.app.SandboxedProcessService;
import org.chromium.content.common.ContentSwitchUtils;
import org.chromium.content_public.browser.ChildProcessImportance;
......@@ -146,10 +145,8 @@ public final class ChildProcessLauncherHelperImpl {
ContentChildProcessConstants.EXTRA_CPU_COUNT, CpuFeatures.getCount());
connectionBundle.putLong(
ContentChildProcessConstants.EXTRA_CPU_FEATURES, CpuFeatures.getMask());
if (LibraryLoader.getInstance().useChromiumLinker()) {
connectionBundle.putBundle(Linker.EXTRA_LINKER_SHARED_RELROS,
Linker.getInstance().getSharedRelros());
}
LibraryLoader.getInstance().getMediator().putSharedRelrosToBundle(
connectionBundle);
}
@Override
......@@ -638,36 +635,13 @@ public final class ChildProcessLauncherHelperImpl {
}
}
private static boolean sLinkerInitialized;
private static long sLinkerLoadAddress;
private static void initLinker() {
assert LauncherThread.runningOnLauncherThread();
if (sLinkerInitialized) return;
if (LibraryLoader.getInstance().useChromiumLinker()) {
sLinkerLoadAddress = Linker.getInstance().getBaseLoadAddress();
if (sLinkerLoadAddress == 0) {
Log.i(TAG, "Shared RELRO support disabled!");
}
}
sLinkerInitialized = true;
}
private static ChromiumLinkerParams getLinkerParamsForNewConnection() {
assert LauncherThread.runningOnLauncherThread();
initLinker();
assert sLinkerInitialized;
if (sLinkerLoadAddress == 0) return null;
return new ChromiumLinkerParams(sLinkerLoadAddress);
}
private static Bundle populateServiceBundle(Bundle bundle) {
ChildProcessCreationParamsImpl.addIntentExtras(bundle);
bundle.putBoolean(ChildProcessConstants.EXTRA_BIND_TO_CALLER,
ChildProcessCreationParamsImpl.getBindToCallerCheck());
ChromiumLinkerParams linkerParams = getLinkerParamsForNewConnection();
if (linkerParams != null) linkerParams.populateBundle(bundle);
MultiProcessMediator m = LibraryLoader.getInstance().getMediator();
m.ensureInitializedInMainProcess();
m.putLoadAddressToBundle(bundle);
return bundle;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment