117 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			117 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			C++
		
	
	
	
//===- ObjectFileTransformer.cpp --------------------------------*- C++ -*-===//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
						|
// See https://llvm.org/LICENSE.txt for license information.
 | 
						|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include <unordered_set>
 | 
						|
 | 
						|
#include "llvm/Object/ELFObjectFile.h"
 | 
						|
#include "llvm/Object/MachOUniversal.h"
 | 
						|
#include "llvm/Object/ObjectFile.h"
 | 
						|
#include "llvm/Support/DataExtractor.h"
 | 
						|
#include "llvm/Support/raw_ostream.h"
 | 
						|
 | 
						|
#include "llvm/DebugInfo/GSYM/ObjectFileTransformer.h"
 | 
						|
#include "llvm/DebugInfo/GSYM/GsymCreator.h"
 | 
						|
 | 
						|
using namespace llvm;
 | 
						|
using namespace gsym;
 | 
						|
 | 
						|
constexpr uint32_t NT_GNU_BUILD_ID_TAG = 0x03;
 | 
						|
 | 
						|
static std::vector<uint8_t> getUUID(const object::ObjectFile &Obj) {
 | 
						|
  // Extract the UUID from the object file
 | 
						|
  std::vector<uint8_t> UUID;
 | 
						|
  if (auto *MachO = dyn_cast<object::MachOObjectFile>(&Obj)) {
 | 
						|
    const ArrayRef<uint8_t> MachUUID = MachO->getUuid();
 | 
						|
    if (!MachUUID.empty())
 | 
						|
      UUID.assign(MachUUID.data(), MachUUID.data() + MachUUID.size());
 | 
						|
  } else if (isa<object::ELFObjectFileBase>(&Obj)) {
 | 
						|
    const StringRef GNUBuildID(".note.gnu.build-id");
 | 
						|
    for (const object::SectionRef &Sect : Obj.sections()) {
 | 
						|
      Expected<StringRef> SectNameOrErr = Sect.getName();
 | 
						|
      if (!SectNameOrErr) {
 | 
						|
        consumeError(SectNameOrErr.takeError());
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      StringRef SectName(*SectNameOrErr);
 | 
						|
      if (SectName != GNUBuildID)
 | 
						|
        continue;
 | 
						|
      StringRef BuildIDData;
 | 
						|
      Expected<StringRef> E = Sect.getContents();
 | 
						|
      if (E)
 | 
						|
        BuildIDData = *E;
 | 
						|
      else {
 | 
						|
        consumeError(E.takeError());
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      DataExtractor Decoder(BuildIDData, Obj.makeTriple().isLittleEndian(), 8);
 | 
						|
      uint64_t Offset = 0;
 | 
						|
      const uint32_t NameSize = Decoder.getU32(&Offset);
 | 
						|
      const uint32_t PayloadSize = Decoder.getU32(&Offset);
 | 
						|
      const uint32_t PayloadType = Decoder.getU32(&Offset);
 | 
						|
      StringRef Name(Decoder.getFixedLengthString(&Offset, NameSize));
 | 
						|
      if (Name == "GNU" && PayloadType == NT_GNU_BUILD_ID_TAG) {
 | 
						|
        Offset = alignTo(Offset, 4);
 | 
						|
        StringRef UUIDBytes(Decoder.getBytes(&Offset, PayloadSize));
 | 
						|
        if (!UUIDBytes.empty()) {
 | 
						|
          auto Ptr = reinterpret_cast<const uint8_t *>(UUIDBytes.data());
 | 
						|
          UUID.assign(Ptr, Ptr + UUIDBytes.size());
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return UUID;
 | 
						|
}
 | 
						|
 | 
						|
llvm::Error ObjectFileTransformer::convert(const object::ObjectFile &Obj,
 | 
						|
                                           raw_ostream &Log,
 | 
						|
                                           GsymCreator &Gsym) {
 | 
						|
  using namespace llvm::object;
 | 
						|
 | 
						|
  const bool IsMachO = isa<MachOObjectFile>(&Obj);
 | 
						|
  const bool IsELF = isa<ELFObjectFileBase>(&Obj);
 | 
						|
 | 
						|
  // Read build ID.
 | 
						|
  Gsym.setUUID(getUUID(Obj));
 | 
						|
 | 
						|
  // Parse the symbol table.
 | 
						|
  size_t NumBefore = Gsym.getNumFunctionInfos();
 | 
						|
  for (const object::SymbolRef &Sym : Obj.symbols()) {
 | 
						|
    Expected<SymbolRef::Type> SymType = Sym.getType();
 | 
						|
    if (!SymType) {
 | 
						|
      consumeError(SymType.takeError());
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    Expected<uint64_t> AddrOrErr = Sym.getValue();
 | 
						|
    if (!AddrOrErr)
 | 
						|
      // TODO: Test this error.
 | 
						|
      return AddrOrErr.takeError();
 | 
						|
 | 
						|
    if (SymType.get() != SymbolRef::Type::ST_Function ||
 | 
						|
        !Gsym.IsValidTextAddress(*AddrOrErr) ||
 | 
						|
        Gsym.hasFunctionInfoForAddress(*AddrOrErr))
 | 
						|
      continue;
 | 
						|
    // Function size for MachO files will be 0
 | 
						|
    constexpr bool NoCopy = false;
 | 
						|
    const uint64_t size = IsELF ? ELFSymbolRef(Sym).getSize() : 0;
 | 
						|
    Expected<StringRef> Name = Sym.getName();
 | 
						|
    if (!Name) {
 | 
						|
      logAllUnhandledErrors(Name.takeError(), Log, "ObjectFileTransformer: ");
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    // Remove the leading '_' character in any symbol names if there is one
 | 
						|
    // for mach-o files.
 | 
						|
    if (IsMachO)
 | 
						|
      Name->consume_front("_");
 | 
						|
    Gsym.addFunctionInfo(
 | 
						|
        FunctionInfo(*AddrOrErr, size, Gsym.insertString(*Name, NoCopy)));
 | 
						|
  }
 | 
						|
  size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore;
 | 
						|
  Log << "Loaded " << FunctionsAddedCount << " functions from symbol table.\n";
 | 
						|
  return Error::success();
 | 
						|
}
 |