elf_stripper.cc revision afa6b8e93a0dc0de33c9d404945c7c5621e20b1a
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "elf_stripper.h"
18
19#include <unistd.h>
20#include <sys/types.h>
21#include <memory>
22#include <vector>
23
24#include "base/logging.h"
25#include "base/stringprintf.h"
26#include "elf_file.h"
27#include "elf_utils.h"
28#include "utils.h"
29
30namespace art {
31
32bool ElfStripper::Strip(File* file, std::string* error_msg) {
33  std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, error_msg));
34  if (elf_file.get() == nullptr) {
35    return false;
36  }
37
38  // ELF files produced by MCLinker look roughly like this
39  //
40  // +------------+
41  // | Elf32_Ehdr | contains number of Elf32_Shdr and offset to first
42  // +------------+
43  // | Elf32_Phdr | program headers
44  // | Elf32_Phdr |
45  // | ...        |
46  // | Elf32_Phdr |
47  // +------------+
48  // | section    | mixture of needed and unneeded sections
49  // +------------+
50  // | section    |
51  // +------------+
52  // | ...        |
53  // +------------+
54  // | section    |
55  // +------------+
56  // | Elf32_Shdr | section headers
57  // | Elf32_Shdr |
58  // | ...        | contains offset to section start
59  // | Elf32_Shdr |
60  // +------------+
61  //
62  // To strip:
63  // - leave the Elf32_Ehdr and Elf32_Phdr values in place.
64  // - walk the sections making a new set of Elf32_Shdr section headers for what we want to keep
65  // - move the sections are keeping up to fill in gaps of sections we want to strip
66  // - write new Elf32_Shdr section headers to end of file, updating Elf32_Ehdr
67  // - truncate rest of file
68  //
69
70  std::vector<Elf32_Shdr> section_headers;
71  std::vector<Elf32_Word> section_headers_original_indexes;
72  section_headers.reserve(elf_file->GetSectionHeaderNum());
73
74
75  Elf32_Shdr* string_section = elf_file->GetSectionNameStringSection();
76  CHECK(string_section != nullptr);
77  for (Elf32_Word i = 0; i < elf_file->GetSectionHeaderNum(); i++) {
78    Elf32_Shdr* sh = elf_file->GetSectionHeader(i);
79    CHECK(sh != nullptr);
80    const char* name = elf_file->GetString(*string_section, sh->sh_name);
81    if (name == nullptr) {
82      CHECK_EQ(0U, i);
83      section_headers.push_back(*sh);
84      section_headers_original_indexes.push_back(0);
85      continue;
86    }
87    if (StartsWith(name, ".debug")
88        || (strcmp(name, ".strtab") == 0)
89        || (strcmp(name, ".symtab") == 0)) {
90      continue;
91    }
92    section_headers.push_back(*sh);
93    section_headers_original_indexes.push_back(i);
94  }
95  CHECK_NE(0U, section_headers.size());
96  CHECK_EQ(section_headers.size(), section_headers_original_indexes.size());
97
98  // section 0 is the NULL section, sections start at offset of first section
99  CHECK(elf_file->GetSectionHeader(1) != nullptr);
100  Elf32_Off offset = elf_file->GetSectionHeader(1)->sh_offset;
101  for (size_t i = 1; i < section_headers.size(); i++) {
102    Elf32_Shdr& new_sh = section_headers[i];
103    Elf32_Shdr* old_sh = elf_file->GetSectionHeader(section_headers_original_indexes[i]);
104    CHECK(old_sh != nullptr);
105    CHECK_EQ(new_sh.sh_name, old_sh->sh_name);
106    if (old_sh->sh_addralign > 1) {
107      offset = RoundUp(offset, old_sh->sh_addralign);
108    }
109    if (old_sh->sh_offset == offset) {
110      // already in place
111      offset += old_sh->sh_size;
112      continue;
113    }
114    // shift section earlier
115    memmove(elf_file->Begin() + offset,
116            elf_file->Begin() + old_sh->sh_offset,
117            old_sh->sh_size);
118    new_sh.sh_offset = offset;
119    offset += old_sh->sh_size;
120  }
121
122  Elf32_Off shoff = offset;
123  size_t section_headers_size_in_bytes = section_headers.size() * sizeof(Elf32_Shdr);
124  memcpy(elf_file->Begin() + offset, &section_headers[0], section_headers_size_in_bytes);
125  offset += section_headers_size_in_bytes;
126
127  elf_file->GetHeader().e_shnum = section_headers.size();
128  elf_file->GetHeader().e_shoff = shoff;
129  int result = ftruncate(file->Fd(), offset);
130  if (result != 0) {
131    *error_msg = StringPrintf("Failed to truncate while stripping ELF file: '%s': %s",
132                              file->GetPath().c_str(), strerror(errno));
133    return false;
134  }
135  return true;
136}
137
138}  // namespace art
139