Home | History | Annotate | Download | only in compiler
      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 
     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