| Brian Carlstrom | 6a47b9d | 2013-05-17 10:58:25 -0700 | [diff] [blame] | 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 |  | 
| Nicolas Geoffray | e84bfb5 | 2014-03-10 11:27:57 +0000 | [diff] [blame] | 19 | #include <unistd.h> | 
 | 20 | #include <sys/types.h> | 
| Brian Carlstrom | 6a47b9d | 2013-05-17 10:58:25 -0700 | [diff] [blame] | 21 | #include <vector> | 
 | 22 |  | 
| Brian Carlstrom | 6a47b9d | 2013-05-17 10:58:25 -0700 | [diff] [blame] | 23 | #include "UniquePtr.h" | 
 | 24 | #include "base/logging.h" | 
 | 25 | #include "elf_file.h" | 
| Nicolas Geoffray | 50cfe74 | 2014-02-19 13:27:42 +0000 | [diff] [blame] | 26 | #include "elf_utils.h" | 
| Brian Carlstrom | 6a47b9d | 2013-05-17 10:58:25 -0700 | [diff] [blame] | 27 | #include "utils.h" | 
 | 28 |  | 
 | 29 | namespace art { | 
 | 30 |  | 
| Ian Rogers | 8d31bbd | 2013-10-13 10:44:14 -0700 | [diff] [blame] | 31 | bool ElfStripper::Strip(File* file, std::string* error_msg) { | 
 | 32 |   UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false, error_msg)); | 
 | 33 |   if (elf_file.get() == nullptr) { | 
 | 34 |     return false; | 
 | 35 |   } | 
| Brian Carlstrom | 6a47b9d | 2013-05-17 10:58:25 -0700 | [diff] [blame] | 36 |  | 
 | 37 |   // ELF files produced by MCLinker look roughly like this | 
 | 38 |   // | 
 | 39 |   // +------------+ | 
 | 40 |   // | Elf32_Ehdr | contains number of Elf32_Shdr and offset to first | 
 | 41 |   // +------------+ | 
 | 42 |   // | Elf32_Phdr | program headers | 
 | 43 |   // | Elf32_Phdr | | 
 | 44 |   // | ...        | | 
 | 45 |   // | Elf32_Phdr | | 
 | 46 |   // +------------+ | 
 | 47 |   // | section    | mixture of needed and unneeded sections | 
 | 48 |   // +------------+ | 
 | 49 |   // | section    | | 
 | 50 |   // +------------+ | 
 | 51 |   // | ...        | | 
 | 52 |   // +------------+ | 
 | 53 |   // | section    | | 
 | 54 |   // +------------+ | 
 | 55 |   // | Elf32_Shdr | section headers | 
 | 56 |   // | Elf32_Shdr | | 
 | 57 |   // | ...        | contains offset to section start | 
 | 58 |   // | Elf32_Shdr | | 
 | 59 |   // +------------+ | 
 | 60 |   // | 
 | 61 |   // To strip: | 
 | 62 |   // - leave the Elf32_Ehdr and Elf32_Phdr values in place. | 
 | 63 |   // - walk the sections making a new set of Elf32_Shdr section headers for what we want to keep | 
 | 64 |   // - move the sections are keeping up to fill in gaps of sections we want to strip | 
 | 65 |   // - write new Elf32_Shdr section headers to end of file, updating Elf32_Ehdr | 
 | 66 |   // - truncate rest of file | 
 | 67 |   // | 
 | 68 |  | 
| Nicolas Geoffray | 50cfe74 | 2014-02-19 13:27:42 +0000 | [diff] [blame] | 69 |   std::vector<Elf32_Shdr> section_headers; | 
 | 70 |   std::vector<Elf32_Word> section_headers_original_indexes; | 
| Brian Carlstrom | 6a47b9d | 2013-05-17 10:58:25 -0700 | [diff] [blame] | 71 |   section_headers.reserve(elf_file->GetSectionHeaderNum()); | 
 | 72 |  | 
 | 73 |  | 
| Nicolas Geoffray | 50cfe74 | 2014-02-19 13:27:42 +0000 | [diff] [blame] | 74 |   Elf32_Shdr& string_section = elf_file->GetSectionNameStringSection(); | 
 | 75 |   for (Elf32_Word i = 0; i < elf_file->GetSectionHeaderNum(); i++) { | 
 | 76 |     Elf32_Shdr& sh = elf_file->GetSectionHeader(i); | 
| Brian Carlstrom | 6a47b9d | 2013-05-17 10:58:25 -0700 | [diff] [blame] | 77 |     const char* name = elf_file->GetString(string_section, sh.sh_name); | 
 | 78 |     if (name == NULL) { | 
 | 79 |       CHECK_EQ(0U, i); | 
 | 80 |       section_headers.push_back(sh); | 
 | 81 |       section_headers_original_indexes.push_back(0); | 
 | 82 |       continue; | 
 | 83 |     } | 
 | 84 |     if (StartsWith(name, ".debug") | 
 | 85 |         || (strcmp(name, ".strtab") == 0) | 
 | 86 |         || (strcmp(name, ".symtab") == 0)) { | 
 | 87 |       continue; | 
 | 88 |     } | 
 | 89 |     section_headers.push_back(sh); | 
 | 90 |     section_headers_original_indexes.push_back(i); | 
 | 91 |   } | 
 | 92 |   CHECK_NE(0U, section_headers.size()); | 
 | 93 |   CHECK_EQ(section_headers.size(), section_headers_original_indexes.size()); | 
 | 94 |  | 
 | 95 |   // section 0 is the NULL section, sections start at offset of first section | 
| Nicolas Geoffray | 50cfe74 | 2014-02-19 13:27:42 +0000 | [diff] [blame] | 96 |   Elf32_Off offset = elf_file->GetSectionHeader(1).sh_offset; | 
| Brian Carlstrom | 6a47b9d | 2013-05-17 10:58:25 -0700 | [diff] [blame] | 97 |   for (size_t i = 1; i < section_headers.size(); i++) { | 
| Nicolas Geoffray | 50cfe74 | 2014-02-19 13:27:42 +0000 | [diff] [blame] | 98 |     Elf32_Shdr& new_sh = section_headers[i]; | 
 | 99 |     Elf32_Shdr& old_sh = elf_file->GetSectionHeader(section_headers_original_indexes[i]); | 
| Brian Carlstrom | 6a47b9d | 2013-05-17 10:58:25 -0700 | [diff] [blame] | 100 |     CHECK_EQ(new_sh.sh_name, old_sh.sh_name); | 
 | 101 |     if (old_sh.sh_addralign > 1) { | 
 | 102 |       offset = RoundUp(offset, old_sh.sh_addralign); | 
 | 103 |     } | 
 | 104 |     if (old_sh.sh_offset == offset) { | 
 | 105 |       // already in place | 
 | 106 |       offset += old_sh.sh_size; | 
 | 107 |       continue; | 
 | 108 |     } | 
 | 109 |     // shift section earlier | 
 | 110 |     memmove(elf_file->Begin() + offset, | 
 | 111 |             elf_file->Begin() + old_sh.sh_offset, | 
 | 112 |             old_sh.sh_size); | 
 | 113 |     new_sh.sh_offset = offset; | 
 | 114 |     offset += old_sh.sh_size; | 
 | 115 |   } | 
 | 116 |  | 
| Nicolas Geoffray | 50cfe74 | 2014-02-19 13:27:42 +0000 | [diff] [blame] | 117 |   Elf32_Off shoff = offset; | 
 | 118 |   size_t section_headers_size_in_bytes = section_headers.size() * sizeof(Elf32_Shdr); | 
| Brian Carlstrom | 6a47b9d | 2013-05-17 10:58:25 -0700 | [diff] [blame] | 119 |   memcpy(elf_file->Begin() + offset, §ion_headers[0], section_headers_size_in_bytes); | 
 | 120 |   offset += section_headers_size_in_bytes; | 
 | 121 |  | 
 | 122 |   elf_file->GetHeader().e_shnum = section_headers.size(); | 
 | 123 |   elf_file->GetHeader().e_shoff = shoff; | 
 | 124 |   int result = ftruncate(file->Fd(), offset); | 
 | 125 |   if (result != 0) { | 
| Ian Rogers | 8d31bbd | 2013-10-13 10:44:14 -0700 | [diff] [blame] | 126 |     *error_msg = StringPrintf("Failed to truncate while stripping ELF file: '%s': %s", | 
 | 127 |                               file->GetPath().c_str(), strerror(errno)); | 
| Brian Carlstrom | 6a47b9d | 2013-05-17 10:58:25 -0700 | [diff] [blame] | 128 |     return false; | 
 | 129 |   } | 
 | 130 |   return true; | 
 | 131 | } | 
 | 132 |  | 
 | 133 | }  // namespace art |