| // 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 "chromiumos-wide-profiling/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 "chromiumos-wide-profiling/buffer_reader.h" |
| #include "chromiumos-wide-profiling/compat/string.h" |
| #include "chromiumos-wide-profiling/compat/test.h" |
| #include "chromiumos-wide-profiling/dso_test_utils.h" |
| #include "chromiumos-wide-profiling/scoped_temp_path.h" |
| #include "chromiumos-wide-profiling/utils.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 |