1 // Copyright 2016 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "dso_test_utils.h" 6 7 #include <elf.h> 8 #include <fcntl.h> 9 #include <gelf.h> 10 #include <libelf.h> 11 #include <string.h> 12 #include <sys/stat.h> 13 #include <sys/types.h> 14 #include <unistd.h> 15 16 #include <vector> 17 18 #include "base/logging.h" 19 #include "binary_data_utils.h" 20 21 namespace quipper { 22 namespace testing { 23 24 namespace { 25 26 // Example string table: 27 // index 0: "\0" 28 // index 1: "value 1" "\0" 29 // index 9: "value 2" "\0" 30 // index 17: "value 3" "\0" 31 class ElfStringTable { 32 public: 33 ElfStringTable() : table_("", 1) {} // index 0 is the empty string. 34 // Returns the index to use in place of the string. 35 GElf_Word Add(string value) { 36 GElf_Word ret = table_.size(); 37 table_.append(value.data(), value.size() + 1); // Include the '\0'. 38 return ret; 39 } 40 41 const string &table() { return table_; } 42 43 private: 44 string table_; 45 }; 46 47 // Helper to control the scope of data in Elf_Data.d_buf added to a Elf_Scn. 48 // Basically, makes sure that the d_buf pointer remains valid, and holds on to a 49 // copy of your buffer so you don't have to. 50 class ElfDataCache { 51 public: 52 Elf_Data *AddDataToSection(Elf_Scn *section, const string &data_str) { 53 Elf_Data *data = elf_newdata(section); 54 CHECK(data) << elf_errmsg(-1); 55 // avoid zero memory allocation 56 char *data_storage = new char[data_str.size() + 1]; 57 cache_.emplace_back(data_storage); 58 memcpy(data_storage, data_str.data(), data_str.size()); 59 data->d_buf = data_storage; 60 data->d_size = data_str.size(); 61 return data; 62 } 63 64 ~ElfDataCache() { 65 for (auto &s : cache_) { 66 delete[] s; 67 } 68 } 69 70 private: 71 std::vector<char *> cache_; 72 }; 73 74 } // namespace 75 76 void WriteElfWithBuildid(string filename, string section_name, string buildid) { 77 std::vector<std::pair<string, string>> section_name_to_buildid{ 78 std::make_pair(section_name, buildid)}; 79 WriteElfWithMultipleBuildids(filename, section_name_to_buildid); 80 } 81 82 void WriteElfWithMultipleBuildids( 83 string filename, 84 const std::vector<std::pair<string, string>> section_buildids) { 85 int fd = open(filename.data(), O_WRONLY | O_CREAT | O_TRUNC, 0660); 86 CHECK_GE(fd, 0) << strerror(errno); 87 88 // ANDROID-CHANGED: Ensure libelf is initialized, as dso_android doesn't. 89 { 90 const unsigned int kElfVersionNone = EV_NONE; // correctly typed. 91 CHECK_NE(kElfVersionNone, elf_version(EV_CURRENT)) << elf_errmsg(-1); 92 } 93 94 Elf *elf = elf_begin(fd, ELF_C_WRITE, nullptr); 95 CHECK(elf) << elf_errmsg(-1); 96 Elf64_Ehdr *elf_header = elf64_newehdr(elf); 97 CHECK(elf_header) << elf_errmsg(-1); 98 elf_header->e_ident[EI_DATA] = ELFDATA2LSB; 99 elf_header->e_machine = EM_X86_64; 100 elf_header->e_version = EV_CURRENT; 101 CHECK(elf_update(elf, ELF_C_NULL) > 0) << elf_errmsg(-1); 102 103 ElfStringTable string_table; 104 ElfDataCache data_cache; 105 106 // Note section(s) 107 for (const auto &entry : section_buildids) { 108 const string §ion_name = entry.first; 109 const string &buildid = entry.second; 110 Elf_Scn *section = elf_newscn(elf); 111 CHECK(section) << elf_errmsg(-1); 112 GElf_Shdr section_header; 113 CHECK(gelf_getshdr(section, §ion_header)) << elf_errmsg(-1); 114 section_header.sh_name = string_table.Add(section_name); 115 section_header.sh_type = SHT_NOTE; 116 CHECK(gelf_update_shdr(section, §ion_header)) << elf_errmsg(-1); 117 118 string note_name = ELF_NOTE_GNU; 119 GElf_Nhdr note_header; 120 note_header.n_namesz = Align<4>(note_name.size()); 121 note_header.n_descsz = Align<4>(buildid.size()); 122 note_header.n_type = NT_GNU_BUILD_ID; 123 string data_str; 124 data_str.append(reinterpret_cast<char *>(¬e_header), 125 sizeof(note_header)); 126 data_str.append(note_name); 127 data_str.append(string(note_header.n_namesz - note_name.size(), '\0')); 128 data_str.append(buildid); 129 data_str.append(string(note_header.n_descsz - buildid.size(), '\0')); 130 Elf_Data *data = data_cache.AddDataToSection(section, data_str); 131 data->d_type = ELF_T_NHDR; 132 } 133 134 // String table section 135 { 136 Elf_Scn *section = elf_newscn(elf); 137 CHECK(section) << elf_errmsg(-1); 138 GElf_Shdr section_header; 139 CHECK(gelf_getshdr(section, §ion_header)) << elf_errmsg(-1); 140 section_header.sh_name = string_table.Add(".shstrtab"); 141 section_header.sh_type = SHT_STRTAB; 142 CHECK(gelf_update_shdr(section, §ion_header)) << elf_errmsg(-1); 143 Elf_Data *data = data_cache.AddDataToSection(section, string_table.table()); 144 data->d_type = ELF_T_BYTE; 145 146 elf_header->e_shstrndx = elf_ndxscn(section); 147 } 148 149 CHECK(elf_update(elf, ELF_C_WRITE) > 0) << elf_errmsg(-1); 150 elf_end(elf); 151 152 close(fd); 153 } 154 155 } // namespace testing 156 } // namespace quipper 157