// Copyright 2016 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "dso_test_utils.h" #include <elf.h> #include <fcntl.h> #include <gelf.h> #include <libelf.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <vector> #include "base/logging.h" #include "binary_data_utils.h" namespace quipper { namespace testing { namespace { // Example string table: // index 0: "\0" // index 1: "value 1" "\0" // index 9: "value 2" "\0" // index 17: "value 3" "\0" class ElfStringTable { public: ElfStringTable() : table_("", 1) {} // index 0 is the empty string. // Returns the index to use in place of the string. GElf_Word Add(string value) { GElf_Word ret = table_.size(); table_.append(value.data(), value.size() + 1); // Include the '\0'. return ret; } const string &table() { return table_; } private: string table_; }; // Helper to control the scope of data in Elf_Data.d_buf added to a Elf_Scn. // Basically, makes sure that the d_buf pointer remains valid, and holds on to a // copy of your buffer so you don't have to. class ElfDataCache { public: Elf_Data *AddDataToSection(Elf_Scn *section, const string &data_str) { Elf_Data *data = elf_newdata(section); CHECK(data) << elf_errmsg(-1); // avoid zero memory allocation char *data_storage = new char[data_str.size() + 1]; cache_.emplace_back(data_storage); memcpy(data_storage, data_str.data(), data_str.size()); data->d_buf = data_storage; data->d_size = data_str.size(); return data; } ~ElfDataCache() { for (auto &s : cache_) { delete[] s; } } private: std::vector<char *> cache_; }; } // namespace void WriteElfWithBuildid(string filename, string section_name, string buildid) { std::vector<std::pair<string, string>> section_name_to_buildid{ std::make_pair(section_name, buildid)}; WriteElfWithMultipleBuildids(filename, section_name_to_buildid); } void WriteElfWithMultipleBuildids( string filename, const std::vector<std::pair<string, string>> section_buildids) { int fd = open(filename.data(), O_WRONLY | O_CREAT | O_TRUNC, 0660); CHECK_GE(fd, 0) << strerror(errno); // ANDROID-CHANGED: Ensure libelf is initialized, as dso_android doesn't. { const unsigned int kElfVersionNone = EV_NONE; // correctly typed. CHECK_NE(kElfVersionNone, elf_version(EV_CURRENT)) << elf_errmsg(-1); } Elf *elf = elf_begin(fd, ELF_C_WRITE, nullptr); CHECK(elf) << elf_errmsg(-1); Elf64_Ehdr *elf_header = elf64_newehdr(elf); CHECK(elf_header) << elf_errmsg(-1); elf_header->e_ident[EI_DATA] = ELFDATA2LSB; elf_header->e_machine = EM_X86_64; elf_header->e_version = EV_CURRENT; CHECK(elf_update(elf, ELF_C_NULL) > 0) << elf_errmsg(-1); ElfStringTable string_table; ElfDataCache data_cache; // Note section(s) for (const auto &entry : section_buildids) { const string §ion_name = entry.first; const string &buildid = entry.second; Elf_Scn *section = elf_newscn(elf); CHECK(section) << elf_errmsg(-1); GElf_Shdr section_header; CHECK(gelf_getshdr(section, §ion_header)) << elf_errmsg(-1); section_header.sh_name = string_table.Add(section_name); section_header.sh_type = SHT_NOTE; CHECK(gelf_update_shdr(section, §ion_header)) << elf_errmsg(-1); string note_name = ELF_NOTE_GNU; GElf_Nhdr note_header; note_header.n_namesz = Align<4>(note_name.size()); note_header.n_descsz = Align<4>(buildid.size()); note_header.n_type = NT_GNU_BUILD_ID; string data_str; data_str.append(reinterpret_cast<char *>(¬e_header), sizeof(note_header)); data_str.append(note_name); data_str.append(string(note_header.n_namesz - note_name.size(), '\0')); data_str.append(buildid); data_str.append(string(note_header.n_descsz - buildid.size(), '\0')); Elf_Data *data = data_cache.AddDataToSection(section, data_str); data->d_type = ELF_T_NHDR; } // String table section { Elf_Scn *section = elf_newscn(elf); CHECK(section) << elf_errmsg(-1); GElf_Shdr section_header; CHECK(gelf_getshdr(section, §ion_header)) << elf_errmsg(-1); section_header.sh_name = string_table.Add(".shstrtab"); section_header.sh_type = SHT_STRTAB; CHECK(gelf_update_shdr(section, §ion_header)) << elf_errmsg(-1); Elf_Data *data = data_cache.AddDataToSection(section, string_table.table()); data->d_type = ELF_T_BYTE; elf_header->e_shstrndx = elf_ndxscn(section); } CHECK(elf_update(elf, ELF_C_WRITE) > 0) << elf_errmsg(-1); elf_end(elf); close(fd); } } // namespace testing } // namespace quipper