101 changes: 101 additions & 0 deletions llvm/lib/ObjCopy/XCOFF/XCOFFReader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//===- XCOFFReader.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 "XCOFFReader.h"

namespace llvm {
namespace objcopy {
namespace xcoff {

using namespace object;

Error XCOFFReader::readSections(Object &Obj) const {
ArrayRef<XCOFFSectionHeader32> Sections = XCOFFObj.sections32();
for (const XCOFFSectionHeader32 &Sec : Sections) {
Section ReadSec;
// Section header.
ReadSec.SectionHeader = Sec;
DataRefImpl SectionDRI;
SectionDRI.p = reinterpret_cast<uintptr_t>(&Sec);

// Section data.
if (Sec.SectionSize) {
Expected<ArrayRef<uint8_t>> ContentsRef =
XCOFFObj.getSectionContents(SectionDRI);
if (!ContentsRef)
return ContentsRef.takeError();
ReadSec.Contents = ContentsRef.get();
}

// Relocations.
if (Sec.NumberOfRelocations) {
auto Relocations =
XCOFFObj.relocations<XCOFFSectionHeader32, XCOFFRelocation32>(Sec);
if (!Relocations)
return Relocations.takeError();
for (const XCOFFRelocation32 &Rel : Relocations.get())
ReadSec.Relocations.push_back(Rel);
}

Obj.Sections.push_back(std::move(ReadSec));
}
return Error::success();
}

Error XCOFFReader::readSymbols(Object &Obj) const {
std::vector<Symbol> Symbols;
Symbols.reserve(XCOFFObj.getNumberOfSymbolTableEntries());
for (SymbolRef Sym : XCOFFObj.symbols()) {
Symbol ReadSym;
DataRefImpl SymbolDRI = Sym.getRawDataRefImpl();
XCOFFSymbolRef SymbolEntRef = XCOFFObj.toSymbolRef(SymbolDRI);
ReadSym.Sym = *SymbolEntRef.getSymbol32();
// Auxiliary entries.
if (SymbolEntRef.getNumberOfAuxEntries()) {
const char *Start = reinterpret_cast<const char *>(
SymbolDRI.p + XCOFF::SymbolTableEntrySize);
Expected<StringRef> RawAuxEntriesOrError = XCOFFObj.getRawData(
Start,
XCOFF::SymbolTableEntrySize * SymbolEntRef.getNumberOfAuxEntries(),
StringRef("symbol"));
if (!RawAuxEntriesOrError)
return RawAuxEntriesOrError.takeError();
ReadSym.AuxSymbolEntries = RawAuxEntriesOrError.get();
}
Obj.Symbols.push_back(std::move(ReadSym));
}
return Error::success();
}

Expected<std::unique_ptr<Object>> XCOFFReader::create() const {
auto Obj = std::make_unique<Object>();
// Only 32-bit supported now.
if (XCOFFObj.is64Bit())
return createStringError(object_error::invalid_file_type,
"64-bit XCOFF is not supported yet");
// Read the file header.
Obj->FileHeader = *XCOFFObj.fileHeader32();
// Read the optional header.
if (XCOFFObj.getOptionalHeaderSize())
Obj->OptionalFileHeader = *XCOFFObj.auxiliaryHeader32();
// Read each section.
Obj->Sections.reserve(XCOFFObj.getNumberOfSections());
if (Error E = readSections(*Obj))
return std::move(E);
// Read each symbol.
Obj->Symbols.reserve(XCOFFObj.getRawNumberOfSymbolTableEntries32());
if (Error E = readSymbols(*Obj))
return std::move(E);
// String table.
Obj->StringTable = XCOFFObj.getStringTable();
return std::move(Obj);
}

} // end namespace xcoff
} // end namespace objcopy
} // end namespace llvm
35 changes: 35 additions & 0 deletions llvm/lib/ObjCopy/XCOFF/XCOFFReader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//===- XCOFFReader.h --------------------------------------------*- 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIB_OBJCOPY_XCOFF_XCOFFREADER_H
#define LLVM_LIB_OBJCOPY_XCOFF_XCOFFREADER_H

#include "XCOFFObject.h"

namespace llvm {
namespace objcopy {
namespace xcoff {

using namespace object;

class XCOFFReader {
public:
explicit XCOFFReader(const XCOFFObjectFile &O) : XCOFFObj(O) {}
Expected<std::unique_ptr<Object>> create() const;

private:
const XCOFFObjectFile &XCOFFObj;
Error readSections(Object &Obj) const;
Error readSymbols(Object &Obj) const;
};

} // end namespace xcoff
} // end namespace objcopy
} // end namespace llvm

#endif // LLVM_LIB_OBJCOPY_XCOFF_XCOFFREADER_H
125 changes: 125 additions & 0 deletions llvm/lib/ObjCopy/XCOFF/XCOFFWriter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//===- XCOFFWriter.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 "llvm/Support/Errc.h"
#include "XCOFFWriter.h"

namespace llvm {
namespace objcopy {
namespace xcoff {

using namespace object;

void XCOFFWriter::finalizeHeaders() {
// File header.
FileSize += sizeof(XCOFFFileHeader32);
// Optional file header.
FileSize += Obj.FileHeader.AuxHeaderSize;
// Section headers.
FileSize += sizeof(XCOFFSectionHeader32) * Obj.Sections.size();
}

void XCOFFWriter::finalizeSections() {
for (const Section &Sec : Obj.Sections) {
// Section data.
FileSize += Sec.Contents.size();
// Relocations.
FileSize +=
Sec.SectionHeader.NumberOfRelocations * sizeof(XCOFFRelocation32);
}
}

void XCOFFWriter::finalizeSymbolStringTable() {
assert(Obj.FileHeader.SymbolTableOffset >= FileSize);
FileSize = Obj.FileHeader.SymbolTableOffset;
// Symbols and auxiliary entries.
FileSize +=
Obj.FileHeader.NumberOfSymTableEntries * XCOFF::SymbolTableEntrySize;
// String table.
FileSize += Obj.StringTable.size();
}

void XCOFFWriter::finalize() {
FileSize = 0;
finalizeHeaders();
finalizeSections();
finalizeSymbolStringTable();
}

void XCOFFWriter::writeHeaders() {
// Write the file header.
uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart());
memcpy(Ptr, &Obj.FileHeader, sizeof(XCOFFFileHeader32));
Ptr += sizeof(XCOFFFileHeader32);

// Write the optional header.
if (Obj.FileHeader.AuxHeaderSize) {
memcpy(Ptr, &Obj.OptionalFileHeader, Obj.FileHeader.AuxHeaderSize);
Ptr += Obj.FileHeader.AuxHeaderSize;
}

// Write section headers.
for (const Section &Sec : Obj.Sections) {
memcpy(Ptr, &Sec.SectionHeader, sizeof(XCOFFSectionHeader32));
Ptr += sizeof(XCOFFSectionHeader32);
}
}

void XCOFFWriter::writeSections() {
// Write section data.
for (const Section &Sec : Obj.Sections) {
uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +
Sec.SectionHeader.FileOffsetToRawData;
Ptr = std::copy(Sec.Contents.begin(), Sec.Contents.end(), Ptr);
}

// Write relocations.
for (const Section &Sec : Obj.Sections) {
uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +
Sec.SectionHeader.FileOffsetToRelocationInfo;
for (const XCOFFRelocation32 &Rel : Sec.Relocations) {
memcpy(Ptr, &Rel, sizeof(XCOFFRelocation32));
Ptr += sizeof(XCOFFRelocation32);
}
}
}

void XCOFFWriter::writeSymbolStringTable() {
// Write symbols.
uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +
Obj.FileHeader.SymbolTableOffset;
for (const Symbol &Sym : Obj.Symbols) {
memcpy(Ptr, &Sym.Sym, XCOFF::SymbolTableEntrySize);
Ptr += XCOFF::SymbolTableEntrySize;
// Auxiliary symbols.
memcpy(Ptr, Sym.AuxSymbolEntries.data(), Sym.AuxSymbolEntries.size());
Ptr += Sym.AuxSymbolEntries.size();
}
// Write the string table.
memcpy(Ptr, Obj.StringTable.data(), Obj.StringTable.size());
Ptr += Obj.StringTable.size();
}

Error XCOFFWriter::write() {
finalize();
Buf = WritableMemoryBuffer::getNewMemBuffer(FileSize);
if (!Buf)
return createStringError(errc::not_enough_memory,
"failed to allocate memory buffer of " +
Twine::utohexstr(FileSize) + " bytes");

writeHeaders();
writeSections();
writeSymbolStringTable();
Out.write(Buf->getBufferStart(), Buf->getBufferSize());
return Error::success();
}

} // end namespace xcoff
} // end namespace objcopy
} // end namespace llvm
48 changes: 48 additions & 0 deletions llvm/lib/ObjCopy/XCOFF/XCOFFWriter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//===- XCOFFWriter.h --------------------------------------------*- 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIB_OBJCOPY_XCOFF_XCOFFWRITER_H
#define LLVM_LIB_OBJCOPY_XCOFF_XCOFFWRITER_H

#include "llvm/Support/MemoryBuffer.h"
#include "XCOFFObject.h"

#include <cstdint>
#include <vector>

namespace llvm {
namespace objcopy {
namespace xcoff {

class XCOFFWriter {
public:
virtual ~XCOFFWriter() {}
XCOFFWriter(Object &Obj, raw_ostream &Out) : Obj(Obj), Out(Out) {}
Error write();

private:
Object &Obj;
raw_ostream &Out;
std::unique_ptr<WritableMemoryBuffer> Buf;
size_t FileSize;

void finalizeHeaders();
void finalizeSections();
void finalizeSymbolStringTable();
void finalize();

void writeHeaders();
void writeSections();
void writeSymbolStringTable();
};

} // end namespace xcoff
} // end namespace objcopy
} // end namespace llvm

#endif // LLVM_LIB_OBJCOPY_XCOFF_XCOFFWRITER_H
13 changes: 13 additions & 0 deletions llvm/lib/Object/XCOFFObjectFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,19 @@ bool XCOFFObjectFile::is64Bit() const {
return Binary::ID_XCOFF64 == getType();
}

Expected<StringRef> XCOFFObjectFile::getRawData(const char *Start,
uint64_t Size,
StringRef Name) const {
uintptr_t StartPtr = reinterpret_cast<uintptr_t>(Start);
// TODO: this path is untested.
if (Error E = Binary::checkOffset(Data, StartPtr, Size))
return createError(toString(std::move(E)) + ": " + Name.data() +
" data with offset 0x" + Twine::utohexstr(StartPtr) +
" and size 0x" + Twine::utohexstr(Size) +
" goes past the end of the file");
return StringRef(Start, Size);
}

uint16_t XCOFFObjectFile::getMagic() const {
return is64Bit() ? fileHeader64()->Magic : fileHeader32()->Magic;
}
Expand Down
31 changes: 31 additions & 0 deletions llvm/test/tools/llvm-objcopy/XCOFF/basic-copy.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# RUN: yaml2obj %s -o %t
# RUN: llvm-objcopy %t %t.out
# RUN: cmp %t %t.out

--- !XCOFF
FileHeader:
MagicNumber: 0x1DF
AuxiliaryHeader:
Magic: 0x10B
Sections:
- Name: .text
Flags: [ STYP_TEXT ]
SectionData: "123456"
- Name: .data
Flags: [ STYP_DATA ]
SectionData: "067891"
Relocations:
- Address: 0x3A
Type: 0x02
Symbols:
- Name: aux_fcn_csect
StorageClass: C_EXT
Type: 0x20
AuxEntries:
- Type: AUX_FCN
- Type: AUX_CSECT
- Name: aux_stat
StorageClass: C_STAT
AuxEntries:
- Type: AUX_STAT
...
47 changes: 47 additions & 0 deletions llvm/test/tools/llvm-objcopy/XCOFF/invalid-read.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
## Check that llvm-objcopy reports a suitable error when it
## encounters invalid input during reading.

## Failed to read section data.
# RUN: yaml2obj %s --docnum=1 -o %t1
# RUN: not llvm-objcopy %t1 %t1.out 2>&1 | FileCheck %s -DFILE=%t1 --check-prefix=ERROR1

# ERROR1: error: '[[FILE]]': The end of the file was unexpectedly encountered: section data with offset 0x70 and size 0x4 goes past the end of the file

--- !XCOFF
FileHeader:
MagicNumber: 0x01DF
Sections:
- SectionData: '00007400'
FileOffsetToData: 0x70

## Failed to read relocations.
# RUN: yaml2obj %s --docnum=2 -o %t2
# RUN: not llvm-objcopy %t2 %t2.out 2>&1 | FileCheck %s -DFILE=%t2 --check-prefix=ERROR2

# ERROR2: error: '[[FILE]]': The end of the file was unexpectedly encountered: relocations with offset 0x3c and size 0x1e go past the end of the file

--- !XCOFF
FileHeader:
MagicNumber: 0x01DF
Sections:
- NumberOfRelocations: 0x3
Relocations:
- Address: 0xE
Symbol: 0x12
Info: 0xF
Type: 0x3

## Failed to read the symbols.
# RUN: yaml2obj %s --docnum=3 -o %t3
# RUN: not llvm-objcopy %t3 %t3.out 2>&1 | FileCheck %s -DFILE=%t3 --check-prefix=ERROR3

# ERROR3: error: '[[FILE]]': The end of the file was unexpectedly encountered: symbol table with offset 0x15 and size 0x24 goes past the end of the file

--- !XCOFF
FileHeader:
MagicNumber: 0x01DF
OffsetToSymbolTable: 0x15
Symbols:
- Name: foo
AuxEntries:
- Type: AUX_CSECT