Commit 10de3904 authored by Denis Solonkov's avatar Denis Solonkov Committed by Commit Bot

Initial commit for library compression project.

This change adds the folder and stub of a compression script for the
project. The overall idea of the project is to reduce the size of the
library by compressing rarely used code and decompressing it on demand.

For more information see README.md and the tracking bug.

Bug: 998082
Change-Id: Idbb303a38cd73b0d31c5ce897e11bfdc3e1243a8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1768715
Commit-Queue: Denis Solonkov <solonkovda@google.com>
Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Reviewed-by: default avatarBoris Sazonov <bsazonov@chromium.org>
Reviewed-by: default avatarAlex Ilin <alexilin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#691627}
parent c9f1387c
alexilin@chromium.org
bsazonov@chromium.org
pasko@chromium.org
# Shared library compression
## Description
This directory contains the shared library compression tool that allows to
reduce the shared library size by compressing non-performance critical code.
The decompression is done on demand by a watcher thread that is set in a
library constructor. This constructor (being a single .c file) should be
included in the library build to be applied to it.
Additional information may be found in the tracking bug:
https://crbug.com/998082
The tool consists of 2 parts:
### Compression script
This script does the compression part. It removes the specified range from
the file and adds compressed version of it instead. It then sets library
constructor's symbols to point at the cutted range and to the compressed
version.
### Library constructor
TODO(https://crbug.com/998082): describe this.
## Usage
Firstly, the library needs to be build with the tool's constructor.
TODO(https://crbug.com/998082): describe this.
After the library build is complete, the compression script must be applied to
it in the following way:
./compress_section.py -i lib.so -o patched_lib.so -l <begin> -r <end>
Where `<begin>` and `<end>` are the file offsets of the part that you want to
compress.
It is important to note that after running the script some of the common ELF
tooling utilities, for example objcopy, may stop working because of the
unusual (but legal) changes made to the library.
#!/usr/bin/env python3
# Copyright 2019 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 script compresses a part of shared library without breaking it.
It compresses the specified file range which will be used by a new library
constructor that uses userfaultfd to intercept access attempts to the range
and decompress its data on demand.
Technically this script does the following steps:
1) Makes a copy of specified range, compresses it and adds it to the binary
as a new section using objcopy.
2) Moves the Phdr section to the end of the file to ease next manipulations on
it.
3) Creates a LOAD segment for the compressed section created at step 1.
4) Splits the LOAD segments so the range lives in its own
LOAD segment.
5) Cuts the range out of the binary, correcting offsets, broken in the
process.
6) Changes cut range LOAD segment by zeroing the file_sz and setting
mem_sz to the original range size, essentially lazily zeroing the space.
7) Changes address of the symbols provided by the library constructor to
point to the cut range and compressed section.
"""
import argparse
import logging
import sys
def _SetupLogging():
logging.basicConfig(
format='%(asctime)s %(filename)s:%(lineno)s %(levelname)s] %(message)s',
datefmt='%H:%M:%S',
level=logging.ERROR)
def _ParseArguments():
parser = argparse.ArgumentParser()
parser.add_argument(
'-i', '--input', help='Shared library to parse', required=True)
parser.add_argument(
'-o', '--output', help='Name of the output file', required=True)
parser.add_argument(
'-l',
'--left_range',
help='Beginning of the target part of the library',
type=int,
required=True)
parser.add_argument(
'-r',
'--right_range',
help='End (inclusive) of the target part of the library',
type=int,
required=True)
return parser.parse_args()
def main():
_SetupLogging()
args = _ParseArguments()
with open(args.input, 'rb') as f:
data = f.read()
data = bytearray(data)
with open(args.output, 'wb') as f:
f.write(data)
return 0
if __name__ == '__main__':
sys.exit(main())
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