blob: b4d94224ecf3375d7c2ca914440254dd85471ca4 [file] [log] [blame]
// Copyright 2019 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/elf_loader/dynamic_section.h"
#include "starboard/common/log.h"
namespace starboard {
namespace elf_loader {
DynamicSection::DynamicSection(Addr base_memory_address,
Dyn* dynamic,
size_t dynamic_count,
Word dynamic_flags)
: base_memory_address_(base_memory_address),
soname_(NULL),
dynamic_(dynamic),
dynamic_count_(dynamic_count),
dynamic_flags_(dynamic_flags),
has_DT_SYMBOLIC_(false),
symbol_table_(NULL),
string_table_(NULL),
preinit_array_(NULL),
preinit_array_count_(0),
init_array_(NULL),
init_array_count_(0),
fini_array_(NULL),
fini_array_count_(0),
init_func_(NULL),
fini_func_(NULL) {}
bool DynamicSection::InitDynamicSection() {
SB_LOG(INFO) << "Dynamic section count=" << dynamic_count_;
for (int i = 0; i < dynamic_count_; i++) {
Addr dyn_value = dynamic_[i].d_un.d_val;
uintptr_t dyn_addr = base_memory_address_ + dynamic_[i].d_un.d_ptr;
SB_LOG(INFO) << "Dynamic tag=" << dynamic_[i].d_tag;
switch (dynamic_[i].d_tag) {
case DT_DEBUG:
// TODO: implement.
break;
case DT_INIT:
SB_LOG(INFO) << " DT_INIT addr=0x" << std::hex << dyn_addr;
init_func_ = reinterpret_cast<linker_function_t>(dyn_addr);
break;
case DT_FINI:
SB_LOG(INFO) << " DT_FINI addr=0x" << std::hex << dyn_addr;
fini_func_ = reinterpret_cast<linker_function_t>(dyn_addr);
break;
case DT_INIT_ARRAY:
SB_LOG(INFO) << " DT_INIT_ARRAY addr=0x" << std::hex << dyn_addr;
init_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
break;
case DT_INIT_ARRAYSZ:
init_array_count_ = dyn_value / sizeof(Addr);
SB_LOG(INFO) << " DT_INIT_ARRAYSZ value=0x" << std::hex << dyn_value
<< " count=" << std::dec << init_array_count_;
break;
case DT_FINI_ARRAY:
SB_LOG(INFO) << " DT_FINI_ARRAY addr=0x" << std::hex << dyn_addr;
fini_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
break;
case DT_FINI_ARRAYSZ:
fini_array_count_ = dyn_value / sizeof(Addr);
SB_LOG(INFO) << " DT_FINI_ARRAYSZ value=0x" << std::hex << dyn_value
<< " count=" << fini_array_count_;
break;
case DT_PREINIT_ARRAY:
SB_LOG(INFO) << " DT_PREINIT_ARRAY addr=0x" << std::hex << dyn_addr;
preinit_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
break;
case DT_PREINIT_ARRAYSZ:
preinit_array_count_ = dyn_value / sizeof(Addr);
SB_LOG(INFO) << " DT_PREINIT_ARRAYSZ addr=" << dyn_addr
<< " count=" << preinit_array_count_;
break;
case DT_SYMBOLIC:
SB_LOG(INFO) << " DT_SYMBOLIC";
has_DT_SYMBOLIC_ = true;
break;
case DT_FLAGS:
if (dyn_value & DF_SYMBOLIC)
has_DT_SYMBOLIC_ = true;
break;
case DT_SONAME:
soname_ = string_table_ + dyn_value;
break;
default:
break;
}
}
return true;
}
bool DynamicSection::InitDynamicSymbols() {
for (int i = 0; i < dynamic_count_; i++) {
Addr dyn_value = dynamic_[i].d_un.d_val;
uintptr_t dyn_addr = base_memory_address_ + dynamic_[i].d_un.d_ptr;
switch (dynamic_[i].d_tag) {
case DT_HASH:
SB_LOG(INFO) << " DT_HASH addr=0x" << std::hex << dyn_addr;
elf_hash_.Init(dyn_addr);
break;
case DT_GNU_HASH:
SB_LOG(INFO) << " DT_GNU_HASH addr=0x" << std::hex << dyn_addr;
gnu_hash_.Init(dyn_addr);
break;
case DT_STRTAB:
SB_LOG(INFO) << " DT_STRTAB addr=0x" << std::hex << dyn_addr;
string_table_ = reinterpret_cast<const char*>(dyn_addr);
break;
case DT_SYMTAB:
SB_LOG(INFO) << " DT_SYMTAB addr=0x" << std::hex << dyn_addr;
symbol_table_ = reinterpret_cast<Sym*>(dyn_addr);
break;
default:
break;
}
}
return true;
}
const Dyn* DynamicSection::GetDynamicTable() {
return dynamic_;
}
size_t DynamicSection::GetDynamicTableSize() {
return dynamic_count_;
}
void DynamicSection::CallConstructors() {
CallFunction(init_func_, "DT_INIT");
for (size_t n = 0; n < init_array_count_; ++n)
CallFunction(init_array_[n], "DT_INIT_ARRAY");
}
void DynamicSection::CallDestructors() {
for (size_t n = fini_array_count_; n > 0; --n) {
CallFunction(fini_array_[n - 1], "DT_FINI_ARRAY");
}
CallFunction(fini_func_, "DT_FINI");
}
void DynamicSection::CallFunction(linker_function_t func,
const char* func_type) {
uintptr_t func_address = reinterpret_cast<uintptr_t>(func);
// On some platforms the entries in the array can be 0 or -1,
// and should be ignored e.g. Android:
// https://android.googlesource.com/platform/bionic/+/android-4.2_r1/linker/README.TXT
if (func_address != 0 && func_address != uintptr_t(-1)) {
func();
}
}
const Sym* DynamicSection::LookupById(size_t symbol_id) const {
// TODO: Calculated the symbol_table size and validation check.
return &symbol_table_[symbol_id];
}
bool DynamicSection::IsWeakById(size_t symbol_id) const {
// TODO: Calculated the symbol_table size and validation check.
return ELF_ST_BIND(symbol_table_[symbol_id].st_info) == STB_WEAK;
}
const char* DynamicSection::LookupNameById(size_t symbol_id) const {
const Sym* sym = LookupById(symbol_id);
// TODO: Confirm that LookupById actually can return NULL.
if (!sym)
return NULL;
return string_table_ + sym->st_name;
}
const Sym* DynamicSection::LookupByName(const char* symbol_name) const {
const Sym* sym =
gnu_hash_.IsValid()
? gnu_hash_.LookupByName(symbol_name, symbol_table_, string_table_)
: elf_hash_.LookupByName(symbol_name, symbol_table_, string_table_);
// Ignore undefined symbols or those that are not global or weak definitions.
if (!sym || sym->st_shndx == SHN_UNDEF)
return NULL;
uint8_t info = ELF_ST_BIND(sym->st_info);
if (info != STB_GLOBAL && info != STB_WEAK)
return NULL;
return sym;
}
} // namespace elf_loader
} // namespace starboard