/* * Copyright (C) 2011 The Android Open Source Project * * 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 #include #include // For the PROT_NONE constant. #include #include "android-base/scopeguard.h" #include "base/file_utils.h" #include "base/mem_map.h" #include "base/unix_file/fd_file.h" #include "base/utils.h" #include "common_compiler_driver_test.h" #include "driver/compiler_driver.h" #include "elf/elf_builder.h" #include "elf_writer_quick.h" #include "oat/elf_file.h" #include "oat/elf_file_impl.h" #include "oat/oat.h" namespace art { namespace linker { class ElfWriterTest : public CommonCompilerDriverTest { protected: void SetUp() override { ReserveImageSpace(); CommonCompilerTest::SetUp(); CreateCompilerDriver(); } void WriteElf(File* oat_file, const std::vector& rodata, const std::vector& text, const std::vector& data_img_rel_ro, size_t data_img_rel_ro_app_image_offset, size_t bss_size, size_t bss_methods_offset, size_t bss_roots_offset, size_t dex_section_size) { std::unique_ptr elf_writer = CreateElfWriterQuick( compiler_driver_->GetCompilerOptions(), oat_file); elf_writer->Start(); OutputStream* rodata_section = elf_writer->StartRoData(); elf_writer->PrepareDynamicSection(rodata.size(), text.size(), data_img_rel_ro.size(), data_img_rel_ro_app_image_offset, bss_size, bss_methods_offset, bss_roots_offset, dex_section_size); ASSERT_TRUE(rodata_section->WriteFully(rodata.data(), rodata.size())); elf_writer->EndRoData(rodata_section); OutputStream* text_section = elf_writer->StartText(); ASSERT_TRUE(text_section->WriteFully(text.data(), text.size())); elf_writer->EndText(text_section); if (!data_img_rel_ro.empty()) { OutputStream* data_img_rel_ro_section = elf_writer->StartDataImgRelRo(); ASSERT_TRUE(data_img_rel_ro_section->WriteFully(data_img_rel_ro.data(), data_img_rel_ro.size())); elf_writer->EndDataImgRelRo(data_img_rel_ro_section); } elf_writer->WriteDynamicSection(); ASSERT_TRUE(elf_writer->End()); } }; static void FindSymbolAddress(File* file, const char* symbol_name, /*out*/ uint8_t** addr) { ASSERT_NE(elf_version(EV_CURRENT), EV_NONE) << "libelf initialization failed: " << elf_errmsg(-1); Elf* elf = elf_begin(file->Fd(), ELF_C_READ, /*ref=*/nullptr); ASSERT_NE(elf, nullptr) << elf_errmsg(-1); auto elf_cleanup = android::base::make_scope_guard([&]() { elf_end(elf); }); Elf_Scn* dyn_scn = nullptr; GElf_Shdr scn_hdr; while ((dyn_scn = elf_nextscn(elf, dyn_scn)) != nullptr) { ASSERT_EQ(gelf_getshdr(dyn_scn, &scn_hdr), &scn_hdr) << elf_errmsg(-1); if (scn_hdr.sh_type == SHT_DYNSYM) { break; } } ASSERT_NE(dyn_scn, nullptr) << "Section SHT_DYNSYM not found"; Elf_Data* data = elf_getdata(dyn_scn, /*data=*/nullptr); // Iterate through dynamic section entries. for (int i = 0; i < scn_hdr.sh_size / scn_hdr.sh_entsize; i++) { GElf_Sym sym; ASSERT_EQ(gelf_getsym(data, i, &sym), &sym) << elf_errmsg(-1); const char* name = elf_strptr(elf, scn_hdr.sh_link, sym.st_name); if (strcmp(name, symbol_name) == 0) { *addr = reinterpret_cast(sym.st_value); break; } } ASSERT_NE(*addr, nullptr) << "Symbol " << symbol_name << "not found"; } TEST_F(ElfWriterTest, dlsym) { std::string elf_location = GetCoreOatLocation(); std::string elf_filename = GetSystemImageFilename(elf_location.c_str(), kRuntimeISA); LOG(INFO) << "elf_filename=" << elf_filename; UnreserveImageSpace(); uint8_t* dl_oatdata = nullptr; uint8_t* dl_oatexec = nullptr; uint8_t* dl_oatlastword = nullptr; std::unique_ptr file(OS::OpenFileForReading(elf_filename.c_str())); ASSERT_TRUE(file.get() != nullptr) << elf_filename; ASSERT_NO_FATAL_FAILURE(FindSymbolAddress(file.get(), "oatdata", &dl_oatdata)); ASSERT_NO_FATAL_FAILURE(FindSymbolAddress(file.get(), "oatexec", &dl_oatexec)); ASSERT_NO_FATAL_FAILURE(FindSymbolAddress(file.get(), "oatlastword", &dl_oatlastword)); { std::string error_msg; std::unique_ptr ef(ElfFile::Open(file.get(), /*low_4gb=*/false, &error_msg)); CHECK(ef.get() != nullptr) << error_msg; size_t size; bool success = ef->GetLoadedSize(&size, &error_msg); CHECK(success) << error_msg; MemMap reservation = MemMap::MapAnonymous("ElfWriterTest#dlsym reservation", RoundUp(size, MemMap::GetPageSize()), PROT_NONE, /*low_4gb=*/true, &error_msg); CHECK(reservation.IsValid()) << error_msg; uint8_t* base = reservation.Begin(); success = ef->Load(/*executable=*/false, /*low_4gb=*/false, &reservation, &error_msg); CHECK(success) << error_msg; CHECK(!reservation.IsValid()); EXPECT_EQ(reinterpret_cast(dl_oatdata) + reinterpret_cast(base), reinterpret_cast(ef->FindDynamicSymbolAddress("oatdata"))); EXPECT_EQ(reinterpret_cast(dl_oatexec) + reinterpret_cast(base), reinterpret_cast(ef->FindDynamicSymbolAddress("oatexec"))); EXPECT_EQ(reinterpret_cast(dl_oatlastword) + reinterpret_cast(base), reinterpret_cast(ef->FindDynamicSymbolAddress("oatlastword"))); } } static void HasSection(File* file, const char* section_name, /*out*/ bool* result) { ASSERT_NE(elf_version(EV_CURRENT), EV_NONE) << "libelf initialization failed: " << elf_errmsg(-1); Elf* elf = elf_begin(file->Fd(), ELF_C_READ, /*ref=*/nullptr); ASSERT_NE(elf, nullptr) << elf_errmsg(-1); auto elf_cleanup = android::base::make_scope_guard([&]() { elf_end(elf); }); size_t shstrndx = 0; ASSERT_EQ(elf_getshdrstrndx(elf, &shstrndx), 0) << elf_errmsg(-1); Elf_Scn* dyn_scn = nullptr; GElf_Shdr scn_hdr; while ((dyn_scn = elf_nextscn(elf, dyn_scn)) != nullptr) { ASSERT_EQ(gelf_getshdr(dyn_scn, &scn_hdr), &scn_hdr) << elf_errmsg(-1); const char* name = elf_strptr(elf, shstrndx, scn_hdr.sh_name); if (strcmp(name, section_name) == 0) { *result = true; return; } } *result = false; } TEST_F(ElfWriterTest, CheckBuildIdPresent) { std::string elf_location = GetCoreOatLocation(); std::string elf_filename = GetSystemImageFilename(elf_location.c_str(), kRuntimeISA); LOG(INFO) << "elf_filename=" << elf_filename; std::unique_ptr file(OS::OpenFileForReading(elf_filename.c_str())); ASSERT_TRUE(file.get() != nullptr); bool result; ASSERT_NO_FATAL_FAILURE(HasSection(file.get(), ".note.gnu.build-id", &result)); EXPECT_TRUE(result); } // Check that dynamic sections (.dynamic, .dynsym, .dynstr, .hash) in an oat file are formed // correctly so that dynamic symbols can be successfully looked up. TEST_F(ElfWriterTest, CheckDynamicSection) { // This function generates an oat file with the specified oat data sizes and offsets and // verifies it: // * Checks that the file can be loaded by the ELF loader. // * Checks that the expected dynamic symbols exist and point to the corresponding data // in the loaded file. // * Checks the alignment of the oat data. // The function returns the number of dynamic symbols (excluding "lastword" ones) in the // generated oat file. auto verify = [this](size_t rodata_size, size_t text_size, size_t data_img_rel_ro_size, size_t data_img_rel_ro_app_image_offset, size_t bss_size, size_t bss_methods_offset, size_t bss_roots_offset, size_t dex_section_size, /*out*/ size_t* number_of_dynamic_symbols) { SCOPED_TRACE(::testing::Message() << "rodata_size: " << rodata_size << ", text_size: " << text_size << ", data_img_rel_ro_size: " << data_img_rel_ro_size << ", data_img_rel_ro_app_image_offset: " << data_img_rel_ro_app_image_offset << ", bss_size: " << bss_size << ", bss_methods_offset: " << bss_methods_offset << ", bss_roots_offset: " << bss_roots_offset << ", dex_section_size: " << dex_section_size); *number_of_dynamic_symbols = 1; // "oatdata". std::vector rodata(rodata_size, 0xAA); std::vector text(text_size, 0xBB); std::vector data_img_rel_ro(data_img_rel_ro_app_image_offset, 0xCC); size_t data_img_rel_ro_app_image_size = data_img_rel_ro_size - data_img_rel_ro_app_image_offset; data_img_rel_ro.insert(data_img_rel_ro.cend(), data_img_rel_ro_app_image_size, 0xDD); ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"); WriteElf(tmp_oat.GetFile(), rodata, text, data_img_rel_ro, data_img_rel_ro_app_image_offset, bss_size, bss_methods_offset, bss_roots_offset, dex_section_size); std::string error_msg; std::unique_ptr ef(ElfFile::Open(tmp_oat.GetFile(), /*low_4gb=*/false, &error_msg)); ASSERT_NE(ef.get(), nullptr) << error_msg; ASSERT_TRUE(ef->Load(/*executable=*/false, /*low_4gb=*/false, /*reservation=*/nullptr, &error_msg)) << error_msg; const uint8_t* oatdata_ptr = ef->FindDynamicSymbolAddress("oatdata"); ASSERT_NE(oatdata_ptr, nullptr); EXPECT_EQ(memcmp(oatdata_ptr, rodata.data(), rodata.size()), 0); size_t page_size = GetPageSizeSlow(); size_t elf_word_size = ef->Is64Bit() ? sizeof(ElfTypes64::Word) : sizeof(ElfTypes32::Word); if (text_size != 0u) { *number_of_dynamic_symbols += 1; const uint8_t* text_ptr = ef->FindDynamicSymbolAddress("oatexec"); ASSERT_NE(text_ptr, nullptr); ASSERT_TRUE(IsAlignedParam(text_ptr, page_size)); EXPECT_EQ(memcmp(text_ptr, text.data(), text.size()), 0); const uint8_t* oatlastword_ptr = ef->FindDynamicSymbolAddress("oatlastword"); ASSERT_NE(oatlastword_ptr, nullptr); EXPECT_EQ(static_cast(oatlastword_ptr - text_ptr), text_size - elf_word_size); } else if (rodata_size != 0u) { const uint8_t* oatlastword_ptr = ef->FindDynamicSymbolAddress("oatlastword"); ASSERT_NE(oatlastword_ptr, nullptr); EXPECT_EQ(static_cast(oatlastword_ptr - oatdata_ptr), rodata_size - elf_word_size); } if (data_img_rel_ro_size != 0u) { *number_of_dynamic_symbols += 1; const uint8_t* oatdataimgrelro_ptr = ef->FindDynamicSymbolAddress("oatdataimgrelro"); ASSERT_NE(oatdataimgrelro_ptr, nullptr); ASSERT_TRUE(IsAlignedParam(oatdataimgrelro_ptr, page_size)); EXPECT_EQ(memcmp(oatdataimgrelro_ptr, data_img_rel_ro.data(), data_img_rel_ro.size()), 0); const uint8_t* oatdataimgrelrolastword_ptr = ef->FindDynamicSymbolAddress("oatdataimgrelrolastword"); ASSERT_NE(oatdataimgrelrolastword_ptr, nullptr); EXPECT_EQ(static_cast(oatdataimgrelrolastword_ptr - oatdataimgrelro_ptr), data_img_rel_ro_size - elf_word_size); if (data_img_rel_ro_app_image_offset != data_img_rel_ro_size) { *number_of_dynamic_symbols += 1; const uint8_t* oatdataimgrelroappimage_ptr = ef->FindDynamicSymbolAddress("oatdataimgrelroappimage"); ASSERT_NE(oatdataimgrelroappimage_ptr, nullptr); EXPECT_EQ(static_cast(oatdataimgrelroappimage_ptr - oatdataimgrelro_ptr), data_img_rel_ro_app_image_offset); } if (bss_size != 0u) { *number_of_dynamic_symbols += 1; const uint8_t* bss_ptr = ef->FindDynamicSymbolAddress("oatbss"); ASSERT_NE(bss_ptr, nullptr); ASSERT_TRUE(IsAlignedParam(bss_ptr, page_size)); if (bss_methods_offset != bss_roots_offset) { *number_of_dynamic_symbols += 1; const uint8_t* oatbssmethods_ptr = ef->FindDynamicSymbolAddress("oatbssmethods"); ASSERT_NE(oatbssmethods_ptr, nullptr); EXPECT_EQ(static_cast(oatbssmethods_ptr - bss_ptr), bss_methods_offset); } if (bss_roots_offset != bss_size) { *number_of_dynamic_symbols += 1; const uint8_t* oatbssroots_ptr = ef->FindDynamicSymbolAddress("oatbssroots"); ASSERT_NE(oatbssroots_ptr, nullptr); EXPECT_EQ(static_cast(oatbssroots_ptr - bss_ptr), bss_roots_offset); } const uint8_t* oatbsslastword_ptr = ef->FindDynamicSymbolAddress("oatbsslastword"); ASSERT_NE(oatbsslastword_ptr, nullptr); EXPECT_EQ(static_cast(oatbsslastword_ptr - bss_ptr), bss_size - elf_word_size); } } if (dex_section_size != 0u) { *number_of_dynamic_symbols += 1; const uint8_t* dex_ptr = ef->FindDynamicSymbolAddress("oatdex"); ASSERT_NE(dex_ptr, nullptr); ASSERT_TRUE(IsAlignedParam(dex_ptr, page_size)); const uint8_t* oatdexlastword_ptr = ef->FindDynamicSymbolAddress("oatdexlastword"); EXPECT_EQ(static_cast(oatdexlastword_ptr - dex_ptr), dex_section_size - elf_word_size); } }; // If a symbol requires some other ones (e.g. kBssMethods requires kBss), // it should be listed after them. enum class Symbol { kRodata, kText, kDataImgRelRo, kDataImgRelRoAppImage, kBss, kBssMethods, kBssRoots, kDex, kLast = kDex }; constexpr size_t kNumberOfSymbols = static_cast(Symbol::kLast) + 1; // Use an unaligned section size to verify that ElfWriter properly aligns sections in this case. // We can use an arbitrary value that is greater than or equal to an ElfWord (4 bytes). constexpr size_t kSectionSize = 127u; // Offset in .data.img.rel.ro section from its beginning. We can use any value in the range // [0, kSectionSize). constexpr size_t kDataImgRelRoAppImageOffset = kSectionSize / 2; // Offsets in .bss from its beginning. We can use any value in the range [0, kSectionSize), // kBssMethodsOffset should be less than or equal to kBssRootsOffset. constexpr size_t kBssMethodsOffset = kSectionSize / 3; constexpr size_t kBssRootsOffset = 2 * kBssMethodsOffset; auto exists = [](Symbol symbol, const std::bitset &symbols) { return symbols.test(static_cast(symbol)); }; auto get_size = [&](Symbol symbol, const std::bitset &symbols) -> size_t { return exists(symbol, symbols) ? kSectionSize : 0; }; std::bitset symbols; symbols.set(); // Check cases that lead to a different number of dynamic symbols in an oat file. // We start with the case where all symbols exist (corresponding to the bitset 11111111) // and continue to the case where only "oatdata" exists: // 11111111 - all symbols exist. // 01111111 - "oatdex" doesn't exist (least significant bit corresponds to "oatdata"). // 00111111 - "oatdex" and "oatbss" don't exist. // ... // 00000001 - only "oatdata" exists. while (symbols.any()) { DCHECK_IMPLIES(exists(Symbol::kDataImgRelRoAppImage, symbols), exists(Symbol::kDataImgRelRo, symbols)); DCHECK_IMPLIES(exists(Symbol::kBssMethods, symbols), exists(Symbol::kBss, symbols)); DCHECK_IMPLIES(exists(Symbol::kBssRoots, symbols), exists(Symbol::kBss, symbols)); DCHECK_IMPLIES(exists(Symbol::kBssRoots, symbols), exists(Symbol::kBssMethods, symbols)); size_t data_img_rel_ro_size = get_size(Symbol::kDataImgRelRo, symbols); size_t bss_size = get_size(Symbol::kBss, symbols); size_t number_of_dynamic_symbols = 0; verify(get_size(Symbol::kRodata, symbols), get_size(Symbol::kText, symbols), data_img_rel_ro_size, exists(Symbol::kDataImgRelRoAppImage, symbols) ? kDataImgRelRoAppImageOffset : data_img_rel_ro_size, bss_size, exists(Symbol::kBssMethods, symbols) ? kBssMethodsOffset : bss_size, exists(Symbol::kBssRoots, symbols) ? kBssRootsOffset : bss_size, get_size(Symbol::kDex, symbols), &number_of_dynamic_symbols); EXPECT_EQ(number_of_dynamic_symbols, symbols.count()) << "number_of_dynamic_symbols: " << number_of_dynamic_symbols << ", symbols: " << symbols; symbols >>= 1; } } } // namespace linker } // namespace art