1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Tool to pack and unpack relative relocations in a shared library.
6//
7// Packing removes relative relocations from .rel.dyn and writes them
8// in a more compact form to .android.rel.dyn.  Unpacking does the reverse.
9//
10// Invoke with -v to trace actions taken when packing or unpacking.
11// Invoke with -p to pad removed relocations with R_*_NONE.  Suppresses
12// shrinking of .rel.dyn.
13// See PrintUsage() below for full usage details.
14//
15// NOTE: Breaks with libelf 0.152, which is buggy.  libelf 0.158 works.
16
17#include <errno.h>
18#include <fcntl.h>
19#include <getopt.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <sys/types.h>
23#include <unistd.h>
24#include <string>
25
26#include "debug.h"
27#include "elf_file.h"
28#include "libelf.h"
29
30namespace {
31
32void PrintUsage(const char* argv0) {
33  std::string temporary = argv0;
34  const size_t last_slash = temporary.find_last_of("/");
35  if (last_slash != temporary.npos) {
36    temporary.erase(0, last_slash + 1);
37  }
38  const char* basename = temporary.c_str();
39
40  printf(
41      "Usage: %s [-u] [-v] [-p] file\n\n"
42      "Pack or unpack relative relocations in a shared library.\n\n"
43      "  -u, --unpack   unpack previously packed relative relocations\n"
44      "  -v, --verbose  trace object file modifications (for debugging)\n"
45      "  -p, --pad      do not shrink relocations, but pad (for debugging)\n\n",
46      basename);
47
48  if (ELF::kMachine == EM_ARM) {
49    printf(
50        "Extracts relative relocations from the .rel.dyn section, packs them\n"
51        "into a more compact format, and stores the packed relocations in\n"
52        ".android.rel.dyn.  Expands .android.rel.dyn to hold the packed\n"
53        "data, and shrinks .rel.dyn by the amount of unpacked data removed\n"
54        "from it.\n\n"
55        "Before being packed, a shared library needs to be prepared by adding\n"
56        "a null .android.rel.dyn section.\n\n"
57        "To pack relocations in a shared library:\n\n"
58        "    echo -n 'NULL' >/tmp/small\n"
59        "    arm-linux-androideabi-objcopy \\\n"
60        "        --add-section .android.rel.dyn=/tmp/small \\\n"
61        "        libchrome.<version>.so\n"
62        "    rm /tmp/small\n"
63        "    %s libchrome.<version>.so\n\n"
64        "To unpack and restore the shared library to its original state:\n\n"
65        "    %s -u libchrome.<version>.so\n"
66        "    arm-linux-androideabi-objcopy \\\n"
67        "        --remove-section=.android.rel.dyn libchrome.<version>.so\n\n",
68        basename, basename);
69  } else if (ELF::kMachine == EM_AARCH64) {
70    printf(
71        "Extracts relative relocations from the .rela.dyn section, packs them\n"
72        "into a more compact format, and stores the packed relocations in\n"
73        ".android.rela.dyn.  Expands .android.rela.dyn to hold the packed\n"
74        "data, and shrinks .rela.dyn by the amount of unpacked data removed\n"
75        "from it.\n\n"
76        "Before being packed, a shared library needs to be prepared by adding\n"
77        "a null .android.rela.dyn section.\n\n"
78        "To pack relocations in a shared library:\n\n"
79        "    echo -n 'NULL' >/tmp/small\n"
80        "    aarch64-linux-android-objcopy \\\n"
81        "        --add-section .android.rela.dyn=/tmp/small \\\n"
82        "        libchrome.<version>.so\n"
83        "    rm /tmp/small\n"
84        "    %s libchrome.<version>.so\n\n"
85        "To unpack and restore the shared library to its original state:\n\n"
86        "    %s -u libchrome.<version>.so\n"
87        "    aarch64-linux-android-objcopy \\\n"
88        "        --remove-section=.android.rela.dyn libchrome.<version>.so\n\n",
89        basename, basename);
90  } else {
91    NOTREACHED();
92  }
93
94  printf(
95      "Debug sections are not handled, so packing should not be used on\n"
96      "shared libraries compiled for debugging or otherwise unstripped.\n");
97}
98
99}  // namespace
100
101int main(int argc, char* argv[]) {
102  bool is_unpacking = false;
103  bool is_verbose = false;
104  bool is_padding = false;
105
106  static const option options[] = {
107    {"unpack", 0, 0, 'u'}, {"verbose", 0, 0, 'v'}, {"pad", 0, 0, 'p'},
108    {"help", 0, 0, 'h'}, {NULL, 0, 0, 0}
109  };
110  bool has_options = true;
111  while (has_options) {
112    int c = getopt_long(argc, argv, "uvph", options, NULL);
113    switch (c) {
114      case 'u':
115        is_unpacking = true;
116        break;
117      case 'v':
118        is_verbose = true;
119        break;
120      case 'p':
121        is_padding = true;
122        break;
123      case 'h':
124        PrintUsage(argv[0]);
125        return 0;
126      case '?':
127        LOG(INFO) << "Try '" << argv[0] << " --help' for more information.";
128        return 1;
129      case -1:
130        has_options = false;
131        break;
132      default:
133        NOTREACHED();
134        return 1;
135    }
136  }
137  if (optind != argc - 1) {
138    LOG(INFO) << "Try '" << argv[0] << " --help' for more information.";
139    return 1;
140  }
141
142  if (elf_version(EV_CURRENT) == EV_NONE) {
143    LOG(WARNING) << "Elf Library is out of date!";
144  }
145
146  LOG(INFO) << "Configured for " << ELF::Machine();
147
148  const char* file = argv[argc - 1];
149  const int fd = open(file, O_RDWR);
150  if (fd == -1) {
151    LOG(ERROR) << file << ": " << strerror(errno);
152    return 1;
153  }
154
155  if (is_verbose)
156    relocation_packer::Logger::SetVerbose(1);
157
158  relocation_packer::ElfFile elf_file(fd);
159  elf_file.SetPadding(is_padding);
160
161  bool status;
162  if (is_unpacking)
163    status = elf_file.UnpackRelocations();
164  else
165    status = elf_file.PackRelocations();
166
167  close(fd);
168
169  if (!status) {
170    LOG(ERROR) << file << ": failed to pack/unpack file";
171    return 1;
172  }
173
174  return 0;
175}
176