Commit b7b4a541 authored by Siddhartha's avatar Siddhartha Committed by Commit Bot

Add util for reading library name from elf binary

1. Reading elf for the current binary could lead to seg fault if the
   full library is not loaded in memory. So, the util only takes mapped
   address and works on it. The caller must ensure the library is
   mapped.
2. Add function to read soname from the .dynamic section in elf file.
3. The Linux executable does not contain so name. So, use malloc_wrapper
   library to test.

BUG=734705

Change-Id: I1327f4786726143daba57a2aaa1eddd608143c04
Reviewed-on: https://chromium-review.googlesource.com/1031854
Commit-Queue: Siddhartha S <ssid@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/master@{#554635}
parent c3f24aa3
......@@ -2545,6 +2545,10 @@ test("base_unittests") {
}
deps += [ "//base/test:malloc_wrapper" ]
defines += [
# This library is used by ElfReaderTest to test reading elf files.
"MALLOC_WRAPPER_LIB=\"${shlib_prefix}malloc_wrapper${shlib_extension}\"",
]
if (!is_component_build) {
# Set rpath to find libmalloc_wrapper.so even in a non-component build.
......
......@@ -21,14 +21,18 @@ namespace {
#if __SIZEOF_POINTER__ == 4
using Ehdr = Elf32_Ehdr;
using Dyn = Elf32_Dyn;
using Half = Elf32_Half;
using Nhdr = Elf32_Nhdr;
using Phdr = Elf32_Phdr;
using Word = Elf32_Word;
#else
using Ehdr = Elf64_Ehdr;
using Dyn = Elf64_Dyn;
using Half = Elf64_Half;
using Nhdr = Elf64_Nhdr;
using Phdr = Elf64_Phdr;
using Word = Elf64_Word;
#endif
using ElfSegment = span<const char>;
......@@ -84,7 +88,7 @@ std::vector<ElfSegment> FindElfSegments(const void* elf_mapped_base,
Optional<std::string> ReadElfBuildId(const void* elf_base) {
// Elf program headers can have multiple PT_NOTE arrays.
std::vector<ElfSegment> segs = FindElfSegments(&elf_base, PT_NOTE);
std::vector<ElfSegment> segs = FindElfSegments(elf_base, PT_NOTE);
if (segs.empty())
return nullopt;
Optional<std::string> id;
......@@ -97,5 +101,32 @@ Optional<std::string> ReadElfBuildId(const void* elf_base) {
return nullopt;
}
Optional<std::string> ReadElfLibraryName(const void* elf_base) {
std::vector<ElfSegment> segs = FindElfSegments(elf_base, PT_DYNAMIC);
if (segs.empty())
return nullopt;
DCHECK_EQ(1u, segs.size());
const ElfSegment& dynamic_seg = segs.front();
const Dyn* dynamic_start = reinterpret_cast<const Dyn*>(dynamic_seg.data());
const Dyn* dynamic_end = reinterpret_cast<const Dyn*>(
dynamic_seg.data() + dynamic_seg.size_bytes());
Optional<std::string> soname;
Word soname_strtab_offset = 0;
const char* strtab_addr = 0;
for (const Dyn* dynamic_iter = dynamic_start; dynamic_iter < dynamic_end;
++dynamic_iter) {
if (dynamic_iter->d_tag == DT_STRTAB) {
strtab_addr =
dynamic_iter->d_un.d_ptr + reinterpret_cast<const char*>(elf_base);
} else if (dynamic_iter->d_tag == DT_SONAME) {
soname_strtab_offset = dynamic_iter->d_un.d_val;
}
}
if (soname_strtab_offset && strtab_addr)
return std::string(strtab_addr + soname_strtab_offset);
return nullopt;
}
} // namespace debug
} // namespace base
......@@ -18,6 +18,10 @@ namespace debug {
// in memory.
Optional<std::string> BASE_EXPORT ReadElfBuildId(const void* elf_base);
// Returns the library name from the ELF file mapped at |elf_base|, if present.
// The caller must ensure that the file is fully mapped in memory.
Optional<std::string> BASE_EXPORT ReadElfLibraryName(const void* elf_base);
} // namespace debug
} // namespace base
......
......@@ -4,7 +4,11 @@
#include "base/debug/elf_reader_linux.h"
#include <dlfcn.h>
#include "base/files/memory_mapped_file.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
extern char __executable_start;
......@@ -28,5 +32,37 @@ TEST(ElfReaderTest, ReadElfBuildId) {
}
#endif
TEST(ElfReaderTest, ReadElfLibraryName) {
#if defined(OS_ANDROID)
// On Android the library loader memory maps the full so file.
const char kLibraryName[] = "lib_base_unittests__library.so";
const void* addr = &__executable_start;
#else
// On Linux the executable does not contain soname and is not mapped till
// dynamic segment. So, use malloc wrapper so file on which the test already
// depends on.
const char kLibraryName[] = MALLOC_WRAPPER_LIB;
// Find any symbol in the loaded file.
void* handle = dlopen(kLibraryName, RTLD_NOW | RTLD_LOCAL);
const void* init_addr = dlsym(handle, "_init");
// Use this symbol to get full path to the loaded library.
Dl_info info;
int res = dladdr(init_addr, &info);
ASSERT_NE(0, res);
std::string filename(info.dli_fname);
EXPECT_FALSE(filename.empty());
EXPECT_NE(std::string::npos, filename.find(kLibraryName));
// Memory map the so file and use it to test reading so name.
MemoryMappedFile file;
file.Initialize(FilePath(filename));
const void* addr = file.data();
#endif
auto name = ReadElfLibraryName(addr);
ASSERT_TRUE(name);
EXPECT_EQ(kLibraryName, *name);
}
} // 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