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