227 lines
8.5 KiB
C++
227 lines
8.5 KiB
C++
//===- Reader.cpp ---------------------------------------------------------===//
|
|
//
|
|
// 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 "Reader.h"
|
|
#include "Object.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/BinaryFormat/COFF.h"
|
|
#include "llvm/Object/COFF.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
|
|
namespace llvm {
|
|
namespace objcopy {
|
|
namespace coff {
|
|
|
|
using namespace object;
|
|
using namespace COFF;
|
|
|
|
Error COFFReader::readExecutableHeaders(Object &Obj) const {
|
|
const dos_header *DH = COFFObj.getDOSHeader();
|
|
Obj.Is64 = COFFObj.is64();
|
|
if (!DH)
|
|
return Error::success();
|
|
|
|
Obj.IsPE = true;
|
|
Obj.DosHeader = *DH;
|
|
if (DH->AddressOfNewExeHeader > sizeof(*DH))
|
|
Obj.DosStub = ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&DH[1]),
|
|
DH->AddressOfNewExeHeader - sizeof(*DH));
|
|
|
|
if (COFFObj.is64()) {
|
|
Obj.PeHeader = *COFFObj.getPE32PlusHeader();
|
|
} else {
|
|
const pe32_header *PE32 = COFFObj.getPE32Header();
|
|
copyPeHeader(Obj.PeHeader, *PE32);
|
|
// The pe32plus_header (stored in Object) lacks the BaseOfData field.
|
|
Obj.BaseOfData = PE32->BaseOfData;
|
|
}
|
|
|
|
for (size_t I = 0; I < Obj.PeHeader.NumberOfRvaAndSize; I++) {
|
|
const data_directory *Dir = COFFObj.getDataDirectory(I);
|
|
if (!Dir)
|
|
return errorCodeToError(object_error::parse_failed);
|
|
Obj.DataDirectories.emplace_back(*Dir);
|
|
}
|
|
return Error::success();
|
|
}
|
|
|
|
Error COFFReader::readSections(Object &Obj) const {
|
|
std::vector<Section> Sections;
|
|
// Section indexing starts from 1.
|
|
for (size_t I = 1, E = COFFObj.getNumberOfSections(); I <= E; I++) {
|
|
Expected<const coff_section *> SecOrErr = COFFObj.getSection(I);
|
|
if (!SecOrErr)
|
|
return SecOrErr.takeError();
|
|
const coff_section *Sec = *SecOrErr;
|
|
Sections.push_back(Section());
|
|
Section &S = Sections.back();
|
|
S.Header = *Sec;
|
|
S.Header.Characteristics &= ~COFF::IMAGE_SCN_LNK_NRELOC_OVFL;
|
|
ArrayRef<uint8_t> Contents;
|
|
if (Error E = COFFObj.getSectionContents(Sec, Contents))
|
|
return E;
|
|
S.setContentsRef(Contents);
|
|
ArrayRef<coff_relocation> Relocs = COFFObj.getRelocations(Sec);
|
|
for (const coff_relocation &R : Relocs)
|
|
S.Relocs.push_back(R);
|
|
if (Expected<StringRef> NameOrErr = COFFObj.getSectionName(Sec))
|
|
S.Name = *NameOrErr;
|
|
else
|
|
return NameOrErr.takeError();
|
|
}
|
|
Obj.addSections(Sections);
|
|
return Error::success();
|
|
}
|
|
|
|
Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const {
|
|
std::vector<Symbol> Symbols;
|
|
Symbols.reserve(COFFObj.getRawNumberOfSymbols());
|
|
ArrayRef<Section> Sections = Obj.getSections();
|
|
for (uint32_t I = 0, E = COFFObj.getRawNumberOfSymbols(); I < E;) {
|
|
Expected<COFFSymbolRef> SymOrErr = COFFObj.getSymbol(I);
|
|
if (!SymOrErr)
|
|
return SymOrErr.takeError();
|
|
COFFSymbolRef SymRef = *SymOrErr;
|
|
|
|
Symbols.push_back(Symbol());
|
|
Symbol &Sym = Symbols.back();
|
|
// Copy symbols from the original form into an intermediate coff_symbol32.
|
|
if (IsBigObj)
|
|
copySymbol(Sym.Sym,
|
|
*reinterpret_cast<const coff_symbol32 *>(SymRef.getRawPtr()));
|
|
else
|
|
copySymbol(Sym.Sym,
|
|
*reinterpret_cast<const coff_symbol16 *>(SymRef.getRawPtr()));
|
|
auto NameOrErr = COFFObj.getSymbolName(SymRef);
|
|
if (!NameOrErr)
|
|
return NameOrErr.takeError();
|
|
Sym.Name = *NameOrErr;
|
|
|
|
ArrayRef<uint8_t> AuxData = COFFObj.getSymbolAuxData(SymRef);
|
|
size_t SymSize = IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16);
|
|
assert(AuxData.size() == SymSize * SymRef.getNumberOfAuxSymbols());
|
|
// The auxillary symbols are structs of sizeof(coff_symbol16) each.
|
|
// In the big object format (where symbols are coff_symbol32), each
|
|
// auxillary symbol is padded with 2 bytes at the end. Copy each
|
|
// auxillary symbol to the Sym.AuxData vector. For file symbols,
|
|
// the whole range of aux symbols are interpreted as one null padded
|
|
// string instead.
|
|
if (SymRef.isFileRecord())
|
|
Sym.AuxFile = StringRef(reinterpret_cast<const char *>(AuxData.data()),
|
|
AuxData.size())
|
|
.rtrim('\0');
|
|
else
|
|
for (size_t I = 0; I < SymRef.getNumberOfAuxSymbols(); I++)
|
|
Sym.AuxData.push_back(AuxData.slice(I * SymSize, sizeof(AuxSymbol)));
|
|
|
|
// Find the unique id of the section
|
|
if (SymRef.getSectionNumber() <=
|
|
0) // Special symbol (undefined/absolute/debug)
|
|
Sym.TargetSectionId = SymRef.getSectionNumber();
|
|
else if (static_cast<uint32_t>(SymRef.getSectionNumber() - 1) <
|
|
Sections.size())
|
|
Sym.TargetSectionId = Sections[SymRef.getSectionNumber() - 1].UniqueId;
|
|
else
|
|
return createStringError(object_error::parse_failed,
|
|
"section number out of range");
|
|
// For section definitions, check if it is comdat associative, and if
|
|
// it is, find the target section unique id.
|
|
const coff_aux_section_definition *SD = SymRef.getSectionDefinition();
|
|
const coff_aux_weak_external *WE = SymRef.getWeakExternal();
|
|
if (SD && SD->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
|
|
int32_t Index = SD->getNumber(IsBigObj);
|
|
if (Index <= 0 || static_cast<uint32_t>(Index - 1) >= Sections.size())
|
|
return createStringError(object_error::parse_failed,
|
|
"unexpected associative section index");
|
|
Sym.AssociativeComdatTargetSectionId = Sections[Index - 1].UniqueId;
|
|
} else if (WE) {
|
|
// This is a raw symbol index for now, but store it in the Symbol
|
|
// until we've added them to the Object, which assigns the final
|
|
// unique ids.
|
|
Sym.WeakTargetSymbolId = WE->TagIndex;
|
|
}
|
|
I += 1 + SymRef.getNumberOfAuxSymbols();
|
|
}
|
|
Obj.addSymbols(Symbols);
|
|
return Error::success();
|
|
}
|
|
|
|
Error COFFReader::setSymbolTargets(Object &Obj) const {
|
|
std::vector<const Symbol *> RawSymbolTable;
|
|
for (const Symbol &Sym : Obj.getSymbols()) {
|
|
RawSymbolTable.push_back(&Sym);
|
|
for (size_t I = 0; I < Sym.Sym.NumberOfAuxSymbols; I++)
|
|
RawSymbolTable.push_back(nullptr);
|
|
}
|
|
for (Symbol &Sym : Obj.getMutableSymbols()) {
|
|
// Convert WeakTargetSymbolId from the original raw symbol index to
|
|
// a proper unique id.
|
|
if (Sym.WeakTargetSymbolId) {
|
|
if (*Sym.WeakTargetSymbolId >= RawSymbolTable.size())
|
|
return createStringError(object_error::parse_failed,
|
|
"weak external reference out of range");
|
|
const Symbol *Target = RawSymbolTable[*Sym.WeakTargetSymbolId];
|
|
if (Target == nullptr)
|
|
return createStringError(object_error::parse_failed,
|
|
"invalid SymbolTableIndex");
|
|
Sym.WeakTargetSymbolId = Target->UniqueId;
|
|
}
|
|
}
|
|
for (Section &Sec : Obj.getMutableSections()) {
|
|
for (Relocation &R : Sec.Relocs) {
|
|
if (R.Reloc.SymbolTableIndex >= RawSymbolTable.size())
|
|
return createStringError(object_error::parse_failed,
|
|
"SymbolTableIndex out of range");
|
|
const Symbol *Sym = RawSymbolTable[R.Reloc.SymbolTableIndex];
|
|
if (Sym == nullptr)
|
|
return createStringError(object_error::parse_failed,
|
|
"invalid SymbolTableIndex");
|
|
R.Target = Sym->UniqueId;
|
|
R.TargetName = Sym->Name;
|
|
}
|
|
}
|
|
return Error::success();
|
|
}
|
|
|
|
Expected<std::unique_ptr<Object>> COFFReader::create() const {
|
|
auto Obj = std::make_unique<Object>();
|
|
|
|
bool IsBigObj = false;
|
|
if (const coff_file_header *CFH = COFFObj.getCOFFHeader()) {
|
|
Obj->CoffFileHeader = *CFH;
|
|
} else {
|
|
const coff_bigobj_file_header *CBFH = COFFObj.getCOFFBigObjHeader();
|
|
if (!CBFH)
|
|
return createStringError(object_error::parse_failed,
|
|
"no COFF file header returned");
|
|
// Only copying the few fields from the bigobj header that we need
|
|
// and won't recreate in the end.
|
|
Obj->CoffFileHeader.Machine = CBFH->Machine;
|
|
Obj->CoffFileHeader.TimeDateStamp = CBFH->TimeDateStamp;
|
|
IsBigObj = true;
|
|
}
|
|
|
|
if (Error E = readExecutableHeaders(*Obj))
|
|
return std::move(E);
|
|
if (Error E = readSections(*Obj))
|
|
return std::move(E);
|
|
if (Error E = readSymbols(*Obj, IsBigObj))
|
|
return std::move(E);
|
|
if (Error E = setSymbolTargets(*Obj))
|
|
return std::move(E);
|
|
|
|
return std::move(Obj);
|
|
}
|
|
|
|
} // end namespace coff
|
|
} // end namespace objcopy
|
|
} // end namespace llvm
|