blob: 0d7d839c022273efb919494a4151d1d621e5ad68 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License v2.0 with LLVM Exceptions (the
// "License"); you may not use this file except in compliance with the
// License. You may obtain a copy of the License at
//
// https://llvm.org/LICENSE.txt
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Aleksei Vetrov
#include "dwarf.h"
#include <elf.h>
#include <elfutils/libdw.h>
#include <fcntl.h>
#include <cstddef>
#include <memory>
#include <string>
#include <vector>
#include "error.h"
namespace stg {
namespace dwarf {
namespace {
constexpr int kReturnOk = 0;
constexpr int kReturnNoEntry = 1;
} // namespace
Handler::Handler(const std::string& path)
: fd_(path.c_str(), O_RDONLY) {
Check(elf_version(EV_CURRENT) != EV_NONE) << "ELF version mismatch";
elf_ = std::unique_ptr<Elf, ElfDeleter>(
elf_begin(fd_.Value(), ELF_C_READ, nullptr));
InitialiseDwarf();
}
Handler::Handler(char* data, size_t size) {
Check(elf_version(EV_CURRENT) != EV_NONE) << "ELF version mismatch";
elf_ = std::unique_ptr<Elf, ElfDeleter>(elf_memory(data, size));
InitialiseDwarf();
}
void Handler::InitialiseDwarf() {
Check(elf_.get()) << "Couldn't open ELF: " << elf_errmsg(-1);
dwarf_ = std::unique_ptr<::Dwarf, DwarfDeleter>(
dwarf_begin_elf(elf_.get(), DWARF_C_READ, nullptr));
Check(dwarf_.get()) << "dwarf_begin_elf returned error: " << dwarf_errmsg(-1);
}
std::vector<Entry> Handler::GetCompilationUnits() {
std::vector<Entry> result;
Dwarf_Off offset = 0;
while (true) {
Dwarf_Off next_offset;
size_t header_size = 0;
int return_code =
dwarf_next_unit(dwarf_.get(), offset, &next_offset, &header_size,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
Check(return_code == kReturnOk || return_code == kReturnNoEntry)
<< "dwarf_next_unit returned error";
if (return_code == kReturnNoEntry) {
break;
}
result.push_back({});
Check(dwarf_offdie(dwarf_.get(), offset + header_size, &result.back().die))
<< "dwarf_offdie returned error";
offset = next_offset;
}
return result;
}
std::vector<Entry> Entry::GetChildren() {
Entry child;
int return_code = dwarf_child(&die, &child.die);
Check(return_code == kReturnOk || return_code == kReturnNoEntry)
<< "dwarf_child returned error";
std::vector<Entry> result;
while (return_code == kReturnOk) {
result.push_back(child);
return_code = dwarf_siblingof(&child.die, &child.die);
Check(return_code == kReturnOk || return_code == kReturnNoEntry)
<< "dwarf_siblingof returned error";
}
return result;
}
int Entry::GetTag() {
return dwarf_tag(&die);
}
} // namespace dwarf
} // namespace stg