1 // Copyright 2016 The Fuchsia 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 <elf-search.h>
6 #include <fbl/auto_call.h>
7 #include <fbl/vector.h>
8 #include <inttypes.h>
9 #include <launchpad/launchpad.h>
10 #include <lib/zx/job.h>
11 #include <lib/zx/port.h>
12 #include <lib/zx/process.h>
13 #include <lib/zx/time.h>
14 #include <stdio.h>
15 #include <unittest/unittest.h>
16 #include <zircon/status.h>
17 #include <zircon/syscalls/port.h>
18 
19 namespace {
20 
WriteHeaders(const ArrayRef<Elf64_Phdr> & phdrs,const zx::vmo & vmo)21 bool WriteHeaders(const ArrayRef<Elf64_Phdr>& phdrs, const zx::vmo& vmo) {
22     BEGIN_HELPER;
23     const Elf64_Ehdr ehdr = {
24         .e_ident = {
25             [EI_MAG0] = ELFMAG0,
26             [EI_MAG1] = ELFMAG1,
27             [EI_MAG2] = ELFMAG2,
28             [EI_MAG3] = ELFMAG3,
29             [EI_CLASS] = ELFCLASS64,
30             [EI_DATA] = ELFDATA2LSB,
31             [EI_VERSION] = EV_CURRENT,
32             [EI_OSABI] = ELFOSABI_NONE,
33         },
34         .e_type = ET_DYN,
35         .e_machine = kNativeElfMachine,
36         .e_version = EV_CURRENT,
37         .e_entry = 0,
38         .e_phoff = sizeof(Elf64_Ehdr),
39         .e_shoff = 0,
40         .e_flags = 0,
41         .e_ehsize = sizeof(Elf64_Ehdr),
42         .e_phentsize = sizeof(Elf64_Phdr),
43         .e_phnum = static_cast<Elf64_Half>(phdrs.size()),
44         .e_shentsize = 0,
45         .e_shnum = 0,
46         .e_shstrndx = 0,
47     };
48     EXPECT_EQ(ZX_OK, vmo.write(&ehdr, 0, sizeof(ehdr)));
49     EXPECT_EQ(ZX_OK, vmo.write(phdrs.begin(), sizeof(ehdr), sizeof(Elf64_Phdr) * phdrs.size()));
50     END_HELPER;
51 }
52 
53 // TODO(jakehehrlich): Switch all uses of uint8_t to std::byte once libc++ lands.
54 
WriteBuildID(ArrayRef<uint8_t> build_id,const zx::vmo & vmo,uint64_t note_offset)55 bool WriteBuildID(ArrayRef<uint8_t> build_id, const zx::vmo& vmo, uint64_t note_offset) {
56     BEGIN_HELPER;
57     uint8_t buf[64];
58     const Elf64_Nhdr nhdr = {
59         .n_namesz = sizeof(ELF_NOTE_GNU),
60         .n_descsz = static_cast<Elf64_Word>(build_id.size()),
61         .n_type = NT_GNU_BUILD_ID,
62     };
63     ASSERT_GT(sizeof(buf), sizeof(nhdr) + sizeof(ELF_NOTE_GNU) + build_id.size());
64     uint64_t note_size = 0;
65     memcpy(buf + note_size, &nhdr, sizeof(nhdr));
66     note_size += sizeof(nhdr);
67     memcpy(buf + note_size, ELF_NOTE_GNU, sizeof(ELF_NOTE_GNU));
68     note_size += sizeof(ELF_NOTE_GNU);
69     memcpy(buf + note_size, build_id.get(), build_id.size());
70     note_size += build_id.size();
71     EXPECT_EQ(ZX_OK, vmo.write(buf, note_offset, note_size));
72     END_HELPER;
73 }
74 
75 struct Module {
76     fbl::StringPiece name;
77     ArrayRef<Elf64_Phdr> phdrs;
78     ArrayRef<uint8_t> build_id;
79     zx::vmo vmo;
80 };
81 
MakeELF(Module * mod)82 bool MakeELF(Module* mod) {
83     BEGIN_HELPER;
84     size_t size = 0;
85     for (const auto& phdr : mod->phdrs) {
86         size = fbl::max(size, phdr.p_offset + phdr.p_filesz);
87     }
88     ASSERT_EQ(ZX_OK, zx::vmo::create(size, 0, &mod->vmo));
89     EXPECT_EQ(ZX_OK, mod->vmo.set_property(ZX_PROP_NAME, mod->name.data(), mod->name.size()));
90     EXPECT_TRUE(WriteHeaders(mod->phdrs, mod->vmo));
91     for (const auto& phdr : mod->phdrs) {
92         if (phdr.p_type == PT_NOTE) {
93             EXPECT_TRUE(WriteBuildID(mod->build_id, mod->vmo, phdr.p_offset));
94         }
95     }
96     END_HELPER;
97 }
98 
MakePhdr(uint32_t type,uint64_t size,uint64_t addr,uint32_t flags,uint32_t align)99 constexpr Elf64_Phdr MakePhdr(uint32_t type, uint64_t size, uint64_t addr, uint32_t flags, uint32_t align) {
100     return Elf64_Phdr{
101         .p_type = type,
102         .p_flags = flags,
103         .p_offset = addr,
104         .p_vaddr = addr,
105         .p_paddr = addr,
106         .p_filesz = size,
107         .p_memsz = size,
108         .p_align = align,
109     };
110 }
111 
GetKoid(const zx::vmo & obj,zx_koid_t * out)112 bool GetKoid(const zx::vmo& obj, zx_koid_t* out) {
113     BEGIN_HELPER;
114     zx_info_handle_basic_t info;
115     ASSERT_EQ(ZX_OK, obj.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), NULL, NULL));
116     *out = info.koid;
117     END_HELPER;
118 }
119 
ElfSearchTest()120 bool ElfSearchTest() {
121     BEGIN_TEST;
122 
123     // Define some dummy modules.
124     constexpr Elf64_Phdr mod0_phdrs[] = {
125         MakePhdr(PT_LOAD, 0x2000, 0, PF_R, 0x1000),
126         MakePhdr(PT_NOTE, 20, 0x1000, PF_R, 4),
127         MakePhdr(PT_LOAD, 0x1000, 0x2000, PF_R | PF_W, 0x1000),
128         MakePhdr(PT_LOAD, 0x1000, 0x3000, PF_R | PF_X, 0x1000)};
129     constexpr uint8_t mod0_build_id[] = {0xde, 0xad, 0xbe, 0xef};
130     constexpr Elf64_Phdr mod1_phdrs[] = {
131         MakePhdr(PT_LOAD, 0x2000, 0x0000, PF_R, 0x1000),
132         MakePhdr(PT_NOTE, 20, 0x1000, PF_R, 4),
133         MakePhdr(PT_LOAD, 0x1000, 0x2000, PF_R | PF_X, 0x1000)};
134     constexpr uint8_t mod1_build_id[] = {0xff, 0xff, 0xff, 0xff};
135     constexpr Elf64_Phdr mod2_phdrs[] = {
136         MakePhdr(PT_LOAD, 0x2000, 0x0000, PF_R, 0x1000),
137         MakePhdr(PT_NOTE, 20, 0x1000, PF_R, 4),
138     };
139     constexpr uint8_t mod2_build_id[] = {0x00, 0x00, 0x00, 0x00};
140     Module mods[3] = {
141         {"mod0", mod0_phdrs, mod0_build_id, {}},
142         {"mod1", mod1_phdrs, mod1_build_id, {}},
143         {"mod2", mod2_phdrs, mod2_build_id, {}},
144     };
145 
146     // Load the modules and get a handle to the process.
147     launchpad_t* lp;
148     launchpad_create(ZX_HANDLE_INVALID, "mod-test", &lp);
149     uintptr_t base, entry;
150     for (auto& mod : mods) {
151         EXPECT_TRUE(MakeELF(&mod));
152         ASSERT_EQ(ZX_OK, launchpad_elf_load_extra(lp, mod.vmo.get(), &base, &entry), launchpad_error_message(lp));
153     }
154     zx::process process;
155     auto ac = fbl::MakeAutoCall([&]() {
156         process.kill();
157     });
158     EXPECT_NE(ZX_HANDLE_INVALID, *process.reset_and_get_address() = launchpad_get_process_handle(lp));
159 
160     // Now loop though everything, checking module info along the way.
161     uint32_t matchCount = 0, moduleCount = 0;
162     zx_status_t status = ForEachModule(process, [&](const ModuleInfo& info) {
163         ++moduleCount;
164         for (const auto& mod : mods) {
165             if (mod.build_id == info.build_id) {
166                 ++matchCount;
167                 char name[ZX_MAX_NAME_LEN];
168                 zx_koid_t vmo_koid = 0;
169                 EXPECT_TRUE(GetKoid(mod.vmo, &vmo_koid));
170                 snprintf(name, sizeof(name), "<VMO#%" PRIu64 "=%s>", vmo_koid, mod.name.data());
171                 EXPECT_TRUE(info.name == name, "expected module names to be the same");
172                 EXPECT_EQ(mod.phdrs.size(), info.phdrs.size(), "expected same number of phdrs");
173             }
174         }
175         EXPECT_EQ(moduleCount, matchCount, "Build for module was not found.");
176     });
177     EXPECT_EQ(ZX_OK, status, zx_status_get_string(status));
178     EXPECT_EQ(moduleCount, fbl::count_of(mods), "Unexpected number of modules found.");
179     END_TEST;
180 }
181 
182 } // namespace
183 
184 BEGIN_TEST_CASE(elf_search_tests)
185 // TODO(jakehehrlich): Not all error cases are tested. Appropriate tests can be
186 // sussed out by looking at coverage results.
RUN_TEST(ElfSearchTest)187 RUN_TEST(ElfSearchTest)
188 END_TEST_CASE(elf_search_tests)
189 
190 int main(int argc, char** argv) {
191     bool success = unittest_run_all_tests(argc, argv);
192     return success ? 0 : -1;
193 }
194