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 
30 namespace art {
31 
Strip(File * file,std::string * error_msg)32 bool 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