// 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.h" #include <elf.h> #include <fcntl.h> #include <gelf.h> #include <libelf.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <vector> #include "base/logging.h" #include "buffer_reader.h" #include "compat/string.h" #include "compat/test.h" #include "dso_test_utils.h" #include "scoped_temp_path.h" namespace quipper { TEST(DsoTest, ReadsBuildId) { InitializeLibelf(); ScopedTempFile elf("/tmp/tempelf."); const string expected_buildid = "\xde\xad\xf0\x0d"; testing::WriteElfWithBuildid(elf.path(), ".note.gnu.build-id", expected_buildid); string buildid; EXPECT_TRUE(ReadElfBuildId(elf.path(), &buildid)); EXPECT_EQ(expected_buildid, buildid); // Repeat with other spellings of the section name: testing::WriteElfWithBuildid(elf.path(), ".notes", expected_buildid); EXPECT_TRUE(ReadElfBuildId(elf.path(), &buildid)); EXPECT_EQ(expected_buildid, buildid); testing::WriteElfWithBuildid(elf.path(), ".note", expected_buildid); EXPECT_TRUE(ReadElfBuildId(elf.path(), &buildid)); EXPECT_EQ(expected_buildid, buildid); } TEST(DsoTest, ReadsBuildId_MissingBuildid) { InitializeLibelf(); ScopedTempFile elf("/tmp/tempelf."); testing::WriteElfWithMultipleBuildids(elf.path(), {/*empty*/}); string buildid; EXPECT_FALSE(ReadElfBuildId(elf.path(), &buildid)); } TEST(DsoTest, ReadsBuildId_WrongSection) { InitializeLibelf(); ScopedTempFile elf("/tmp/tempelf."); testing::WriteElfWithBuildid(elf.path(), ".unexpected-section", "blah"); string buildid; EXPECT_FALSE(ReadElfBuildId(elf.path(), &buildid)); } TEST(DsoTest, ReadsBuildId_PrefersGnuBuildid) { InitializeLibelf(); ScopedTempFile elf("/tmp/tempelf."); const string buildid_gnu = "\xde\xad\xf0\x0d"; const string buildid_notes = "\xc0\xde\xf0\x0d"; const string buildid_note = "\xfe\xed\xba\xad"; std::vector<std::pair<string, string>> section_buildids{ std::make_pair(".notes", buildid_notes), std::make_pair(".note", buildid_note), std::make_pair(".note.gnu.build-id", buildid_gnu), }; testing::WriteElfWithMultipleBuildids(elf.path(), section_buildids); string buildid; EXPECT_TRUE(ReadElfBuildId(elf.path(), &buildid)); EXPECT_EQ(buildid_gnu, buildid); // Also prefer ".notes" over ".note" section_buildids = { std::make_pair(".note", buildid_note), std::make_pair(".notes", buildid_notes), }; testing::WriteElfWithMultipleBuildids(elf.path(), section_buildids); EXPECT_TRUE(ReadElfBuildId(elf.path(), &buildid)); EXPECT_EQ(buildid_notes, buildid); } TEST(DsoTest, ReadsSysfsModuleBuildidNote) { // Mimic contents of a /sys/module/<name>/notes/.note.gnu.build-id file. const size_t namesz = 4; const size_t descsz = 0x14; const GElf_Nhdr note_header = { .n_namesz = namesz, .n_descsz = descsz, .n_type = NT_GNU_BUILD_ID, }; const char note_name[namesz] = ELF_NOTE_GNU; const char note_desc[descsz]{ // Note \0 here. This is not null-terminated. '\x1c', '\0', '\x69', '\x27', '\x15', '\x26', '\x6b', '\xe7', '\xcc', '\x69', '\x2c', '\x12', '\xe8', '\x09', '\x20', '\x18', '\x03', '\x5b', '\xb6', '\x4f', }; string data; data.append(reinterpret_cast<const char*>(¬e_header), sizeof(note_header)); data.append(note_name, sizeof(note_name)); data.append(note_desc, sizeof(note_desc)); ASSERT_EQ(0x24, data.size()) << "Sanity"; BufferReader data_reader(data.data(), data.size()); string buildid; EXPECT_TRUE(ReadBuildIdNote(&data_reader, &buildid)); EXPECT_EQ(string(note_desc, sizeof(note_desc)), buildid); } } // namespace quipper