Home | History | Annotate | Download | only in quipper
      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 &section_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, &section_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, &section_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 *>(&note_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, &section_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, &section_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