Commit 61248f1a authored by Siddhartha's avatar Siddhartha Committed by Commit Bot

Add utils to read elf binary and get Build id

Adds code to parse elf binary to get build id from
.note.gnu.build-id section. The build ID is needed to symbolize
heap dumps from the users.

BUG=734705

Change-Id: If4365e232c060ba96071ba1e1c43618f9807e39c
Reviewed-on: https://chromium-review.googlesource.com/1028995
Commit-Queue: Siddhartha S <ssid@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/master@{#554304}
parent fae8443c
......@@ -1164,7 +1164,11 @@ jumbo_component("base") {
]
if (is_linux) {
sources += [ "base_paths_posix.cc" ]
sources += [
"base_paths_posix.cc",
"debug/elf_reader_linux.cc",
"debug/elf_reader_linux.h",
]
}
}
......@@ -1264,6 +1268,8 @@ jumbo_component("base") {
# Android uses some Linux sources, put those back.
set_sources_assignment_filter([])
sources += [
"debug/elf_reader_linux.cc",
"debug/elf_reader_linux.h",
"debug/proc_maps_linux.cc",
"debug/proc_maps_linux.h",
"files/file_path_watcher_linux.cc",
......@@ -2137,6 +2143,7 @@ test("base_unittests") {
"debug/alias_unittest.cc",
"debug/crash_logging_unittest.cc",
"debug/debugger_unittest.cc",
"debug/elf_reader_linux_unittest.cc",
"debug/leak_tracker_unittest.cc",
"debug/proc_maps_linux_unittest.cc",
"debug/stack_trace_unittest.cc",
......@@ -2578,6 +2585,7 @@ test("base_unittests") {
deps += [ "//testing/android/native_test:native_test_native_code" ]
set_sources_assignment_filter([])
sources += [
"debug/elf_reader_linux_unittest.cc",
"debug/proc_maps_linux_unittest.cc",
"trace_event/trace_event_android_unittest.cc",
]
......
// Copyright 2018 The Chromium 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 "base/debug/elf_reader_linux.h"
#include <arpa/inet.h>
#include <elf.h>
#include <vector>
#include "base/bits.h"
#include "base/containers/span.h"
#include "base/sha1.h"
#include "base/strings/stringprintf.h"
extern char __executable_start;
namespace base {
namespace debug {
namespace {
#if __SIZEOF_POINTER__ == 4
using Ehdr = Elf32_Ehdr;
using Half = Elf32_Half;
using Nhdr = Elf32_Nhdr;
using Phdr = Elf32_Phdr;
#else
using Ehdr = Elf64_Ehdr;
using Half = Elf64_Half;
using Nhdr = Elf64_Nhdr;
using Phdr = Elf64_Phdr;
#endif
using ElfSegment = span<const char>;
Optional<std::string> ElfSegmentBuildIDNoteAsString(const ElfSegment& segment) {
const void* section_end = segment.data() + segment.size_bytes();
const Nhdr* note_header = reinterpret_cast<const Nhdr*>(segment.data());
while (note_header < section_end) {
if (note_header->n_type == NT_GNU_BUILD_ID)
break;
note_header = reinterpret_cast<const Nhdr*>(
reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) +
bits::Align(note_header->n_namesz, 4) +
bits::Align(note_header->n_descsz, 4));
}
if (note_header >= section_end || note_header->n_descsz != kSHA1Length)
return nullopt;
const uint8_t* guid = reinterpret_cast<const uint8_t*>(note_header) +
sizeof(Nhdr) + bits::Align(note_header->n_namesz, 4);
uint32_t dword = htonl(*reinterpret_cast<const int32_t*>(guid));
uint16_t word1 = htons(*reinterpret_cast<const int16_t*>(guid + 4));
uint16_t word2 = htons(*reinterpret_cast<const int16_t*>(guid + 6));
std::string identifier;
identifier.reserve(kSHA1Length * 2); // as hex string
SStringPrintf(&identifier, "%08X%04X%04X", dword, word1, word2);
for (size_t i = 8; i < note_header->n_descsz; ++i)
StringAppendF(&identifier, "%02X", guid[i]);
return identifier;
}
std::vector<ElfSegment> FindElfSegments(const char* elf_base,
uint32_t segment_type) {
if (strncmp(static_cast<const char*>(elf_base), ELFMAG, SELFMAG) != 0)
return std::vector<ElfSegment>();
const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
const Phdr* phdrs =
reinterpret_cast<const Phdr*>(elf_base + elf_header->e_phoff);
std::vector<ElfSegment> segments;
for (Half i = 0; i < elf_header->e_phnum; ++i) {
if (phdrs[i].p_type == segment_type)
segments.push_back({elf_base + phdrs[i].p_offset, phdrs[i].p_filesz});
}
return segments;
}
} // namespace
Optional<std::string> ReadElfBuildId() {
// Elf program headers can have multiple PT_NOTE arrays.
std::vector<ElfSegment> segs = FindElfSegments(&__executable_start, PT_NOTE);
if (segs.empty())
return nullopt;
Optional<std::string> id;
for (const ElfSegment& seg : segs) {
id = ElfSegmentBuildIDNoteAsString(seg);
if (id)
return id;
}
return nullopt;
}
} // namespace debug
} // namespace base
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DEBUG_ELF_READER_LINUX_H_
#define BASE_DEBUG_ELF_READER_LINUX_H_
#include <string>
#include "base/base_export.h"
#include "base/optional.h"
namespace base {
namespace debug {
// Returns the ELF section .note.gnu.build-id from current executable file, if
// present.
Optional<std::string> BASE_EXPORT ReadElfBuildId();
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_ELF_READER_LINUX_H_
// Copyright 2018 The Chromium 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 "base/debug/elf_reader_linux.h"
#include "base/strings/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace debug {
// The linker flag --build-id is passed only on official builds. Clang does not
// enable it by default and we do not have build id section in non-official
// builds.
#if defined(OFFICIAL_BUILD)
TEST(ElfReaderTest, ReadElfBuildId) {
Optional<std::string> build_id = ReadElfBuildId();
ASSERT_TRUE(build_id);
const size_t kGuidBytes = 20;
EXPECT_EQ(2 * kGuidBytes, build_id.value().size());
for (char c : *build_id) {
EXPECT_TRUE(IsHexDigit(c));
EXPECT_FALSE(IsAsciiLower(c));
}
}
#endif
} // namespace debug
} // namespace base
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment