elf_fixup.cc revision 700a402244a1a423da4f3ba8032459f4b65fa18f
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_fixup.h"
18
19#include <inttypes.h>
20#include <memory>
21
22#include "base/logging.h"
23#include "base/stringprintf.h"
24#include "elf_file.h"
25#include "elf_writer.h"
26
27namespace art {
28
29static const bool DEBUG_FIXUP = false;
30
31bool ElfFixup::Fixup(File* file, uintptr_t oat_data_begin) {
32  std::string error_msg;
33  std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, &error_msg));
34  CHECK(elf_file.get() != nullptr) << error_msg;
35
36  // Lookup "oatdata" symbol address.
37  Elf32_Addr oatdata_address = ElfWriter::GetOatDataAddress(elf_file.get());
38  Elf32_Off base_address = oat_data_begin - oatdata_address;
39
40  if (!FixupDynamic(*elf_file.get(), base_address)) {
41      LOG(WARNING) << "Failed fo fixup .dynamic in " << file->GetPath();
42      return false;
43  }
44  if (!FixupSectionHeaders(*elf_file.get(), base_address)) {
45      LOG(WARNING) << "Failed fo fixup section headers in " << file->GetPath();
46      return false;
47  }
48  if (!FixupProgramHeaders(*elf_file.get(), base_address)) {
49      LOG(WARNING) << "Failed fo fixup program headers in " << file->GetPath();
50      return false;
51  }
52  if (!FixupSymbols(*elf_file.get(), base_address, true)) {
53      LOG(WARNING) << "Failed fo fixup .dynsym in " << file->GetPath();
54      return false;
55  }
56  if (!FixupSymbols(*elf_file.get(), base_address, false)) {
57      LOG(WARNING) << "Failed fo fixup .symtab in " << file->GetPath();
58      return false;
59  }
60  if (!FixupRelocations(*elf_file.get(), base_address)) {
61      LOG(WARNING) << "Failed fo fixup .rel.dyn in " << file->GetPath();
62      return false;
63  }
64  return true;
65}
66
67
68bool ElfFixup::FixupDynamic(ElfFile& elf_file, uintptr_t base_address) {
69  for (Elf32_Word i = 0; i < elf_file.GetDynamicNum(); i++) {
70    Elf32_Dyn& elf_dyn = elf_file.GetDynamic(i);
71    Elf32_Word d_tag = elf_dyn.d_tag;
72    bool elf_dyn_needs_fixup = false;
73    switch (d_tag) {
74      // case 1: well known d_tag values that imply Elf32_Dyn.d_un contains an address in d_ptr
75      case DT_PLTGOT:
76      case DT_HASH:
77      case DT_STRTAB:
78      case DT_SYMTAB:
79      case DT_RELA:
80      case DT_INIT:
81      case DT_FINI:
82      case DT_REL:
83      case DT_DEBUG:
84      case DT_JMPREL: {
85        elf_dyn_needs_fixup = true;
86        break;
87      }
88      // d_val or ignored values
89      case DT_NULL:
90      case DT_NEEDED:
91      case DT_PLTRELSZ:
92      case DT_RELASZ:
93      case DT_RELAENT:
94      case DT_STRSZ:
95      case DT_SYMENT:
96      case DT_SONAME:
97      case DT_RPATH:
98      case DT_SYMBOLIC:
99      case DT_RELSZ:
100      case DT_RELENT:
101      case DT_PLTREL:
102      case DT_TEXTREL:
103      case DT_BIND_NOW:
104      case DT_INIT_ARRAYSZ:
105      case DT_FINI_ARRAYSZ:
106      case DT_RUNPATH:
107      case DT_FLAGS: {
108        break;
109      }
110      // boundary values that should not be used
111      case DT_ENCODING:
112      case DT_LOOS:
113      case DT_HIOS:
114      case DT_LOPROC:
115      case DT_HIPROC: {
116        LOG(FATAL) << "Illegal d_tag value 0x" << std::hex << d_tag;
117        break;
118      }
119      default: {
120        // case 2: "regular" DT_* ranges where even d_tag values imply an address in d_ptr
121        if ((DT_ENCODING  < d_tag && d_tag < DT_LOOS)
122            || (DT_LOOS   < d_tag && d_tag < DT_HIOS)
123            || (DT_LOPROC < d_tag && d_tag < DT_HIPROC)) {
124          // Special case for MIPS which breaks the regular rules between DT_LOPROC and DT_HIPROC
125          if (elf_file.GetHeader().e_machine == EM_MIPS) {
126            switch (d_tag) {
127              case DT_MIPS_RLD_VERSION:
128              case DT_MIPS_TIME_STAMP:
129              case DT_MIPS_ICHECKSUM:
130              case DT_MIPS_IVERSION:
131              case DT_MIPS_FLAGS:
132              case DT_MIPS_LOCAL_GOTNO:
133              case DT_MIPS_CONFLICTNO:
134              case DT_MIPS_LIBLISTNO:
135              case DT_MIPS_SYMTABNO:
136              case DT_MIPS_UNREFEXTNO:
137              case DT_MIPS_GOTSYM:
138              case DT_MIPS_HIPAGENO: {
139                break;
140              }
141              case DT_MIPS_BASE_ADDRESS:
142              case DT_MIPS_CONFLICT:
143              case DT_MIPS_LIBLIST:
144              case DT_MIPS_RLD_MAP: {
145                elf_dyn_needs_fixup = true;
146                break;
147              }
148              default: {
149                LOG(FATAL) << "Unknown MIPS d_tag value 0x" << std::hex << d_tag;
150                break;
151              }
152            }
153          } else if ((elf_dyn.d_tag % 2) == 0) {
154            elf_dyn_needs_fixup = true;
155          }
156        } else {
157          LOG(FATAL) << "Unknown d_tag value 0x" << std::hex << d_tag;
158        }
159        break;
160      }
161    }
162    if (elf_dyn_needs_fixup) {
163      uint32_t d_ptr = elf_dyn.d_un.d_ptr;
164      if (DEBUG_FIXUP) {
165        LOG(INFO) << StringPrintf("In %s moving Elf32_Dyn[%d] from 0x%08x to 0x%08" PRIxPTR,
166                                  elf_file.GetFile().GetPath().c_str(), i,
167                                  d_ptr, d_ptr + base_address);
168      }
169      d_ptr += base_address;
170      elf_dyn.d_un.d_ptr = d_ptr;
171    }
172  }
173  return true;
174}
175
176bool ElfFixup::FixupSectionHeaders(ElfFile& elf_file, uintptr_t base_address) {
177  for (Elf32_Word i = 0; i < elf_file.GetSectionHeaderNum(); i++) {
178    Elf32_Shdr& sh = elf_file.GetSectionHeader(i);
179    // 0 implies that the section will not exist in the memory of the process
180    if (sh.sh_addr == 0) {
181      continue;
182    }
183    if (DEBUG_FIXUP) {
184      LOG(INFO) << StringPrintf("In %s moving Elf32_Shdr[%d] from 0x%08x to 0x%08" PRIxPTR,
185                                elf_file.GetFile().GetPath().c_str(), i,
186                                sh.sh_addr, sh.sh_addr + base_address);
187    }
188    sh.sh_addr += base_address;
189  }
190  return true;
191}
192
193bool ElfFixup::FixupProgramHeaders(ElfFile& elf_file, uintptr_t base_address) {
194  // TODO: ELFObjectFile doesn't have give to Elf32_Phdr, so we do that ourselves for now.
195  for (Elf32_Word i = 0; i < elf_file.GetProgramHeaderNum(); i++) {
196    Elf32_Phdr& ph = elf_file.GetProgramHeader(i);
197    CHECK_EQ(ph.p_vaddr, ph.p_paddr) << elf_file.GetFile().GetPath() << " i=" << i;
198    CHECK((ph.p_align == 0) || (0 == ((ph.p_vaddr - ph.p_offset) & (ph.p_align - 1))))
199            << elf_file.GetFile().GetPath() << " i=" << i;
200    if (DEBUG_FIXUP) {
201      LOG(INFO) << StringPrintf("In %s moving Elf32_Phdr[%d] from 0x%08x to 0x%08" PRIxPTR,
202                                elf_file.GetFile().GetPath().c_str(), i,
203                                ph.p_vaddr, ph.p_vaddr + base_address);
204    }
205    ph.p_vaddr += base_address;
206    ph.p_paddr += base_address;
207    CHECK((ph.p_align == 0) || (0 == ((ph.p_vaddr - ph.p_offset) & (ph.p_align - 1))))
208            << elf_file.GetFile().GetPath() << " i=" << i;
209  }
210  return true;
211}
212
213bool ElfFixup::FixupSymbols(ElfFile& elf_file, uintptr_t base_address, bool dynamic) {
214  Elf32_Word section_type = dynamic ? SHT_DYNSYM : SHT_SYMTAB;
215  // TODO: Unfortunate ELFObjectFile has protected symbol access, so use ElfFile
216  Elf32_Shdr* symbol_section = elf_file.FindSectionByType(section_type);
217  if (symbol_section == NULL) {
218    // file is missing optional .symtab
219    CHECK(!dynamic) << elf_file.GetFile().GetPath();
220    return true;
221  }
222  for (uint32_t i = 0; i < elf_file.GetSymbolNum(*symbol_section); i++) {
223    Elf32_Sym& symbol = elf_file.GetSymbol(section_type, i);
224    if (symbol.st_value != 0) {
225      if (DEBUG_FIXUP) {
226        LOG(INFO) << StringPrintf("In %s moving Elf32_Sym[%d] from 0x%08x to 0x%08" PRIxPTR,
227                                  elf_file.GetFile().GetPath().c_str(), i,
228                                  symbol.st_value, symbol.st_value + base_address);
229      }
230      symbol.st_value += base_address;
231    }
232  }
233  return true;
234}
235
236bool ElfFixup::FixupRelocations(ElfFile& elf_file, uintptr_t base_address) {
237  for (Elf32_Word i = 0; i < elf_file.GetSectionHeaderNum(); i++) {
238    Elf32_Shdr& sh = elf_file.GetSectionHeader(i);
239    if (sh.sh_type == SHT_REL) {
240      for (uint32_t i = 0; i < elf_file.GetRelNum(sh); i++) {
241        Elf32_Rel& rel = elf_file.GetRel(sh, i);
242        if (DEBUG_FIXUP) {
243          LOG(INFO) << StringPrintf("In %s moving Elf32_Rel[%d] from 0x%08x to 0x%08" PRIxPTR,
244                                    elf_file.GetFile().GetPath().c_str(), i,
245                                    rel.r_offset, rel.r_offset + base_address);
246        }
247        rel.r_offset += base_address;
248      }
249    } else if (sh.sh_type == SHT_RELA) {
250      for (uint32_t i = 0; i < elf_file.GetRelaNum(sh); i++) {
251        Elf32_Rela& rela = elf_file.GetRela(sh, i);
252        if (DEBUG_FIXUP) {
253          LOG(INFO) << StringPrintf("In %s moving Elf32_Rela[%d] from 0x%08x to 0x%08" PRIxPTR,
254                                    elf_file.GetFile().GetPath().c_str(), i,
255                                    rela.r_offset, rela.r_offset + base_address);
256        }
257        rela.r_offset += base_address;
258      }
259    }
260  }
261  return true;
262}
263
264}  // namespace art
265