Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp

llvmorg-10.0.1-rc2-0-g77d76b71d7d.

Also add a few more llvm utilities under WITH_CLANG_EXTRAS:

* llvm-dwp, a utility for merging DWARF 5 Split DWARF .dwo files into
  .dwp (DWARF package files)
* llvm-size, a size(1) replacement
* llvm-strings, a strings(1) replacement

MFC after:	3 weeks
This commit is contained in:
dim 2020-06-28 07:43:43 +00:00
commit 1923ebb416
27 changed files with 2524 additions and 88 deletions

View File

@ -864,7 +864,8 @@ llvm/tools/llvm-dis/LLVMBuild.txt
llvm/tools/llvm-dwarfdump/CMakeLists.txt
llvm/tools/llvm-dwarfdump/LLVMBuild.txt
llvm/tools/llvm-dwarfdump/fuzzer/
llvm/tools/llvm-dwp/
llvm/tools/llvm-dwp/CMakeLists.txt
llvm/tools/llvm-dwp/LLVMBuild.txt
llvm/tools/llvm-elfabi/
llvm/tools/llvm-exegesis/
llvm/tools/llvm-extract/CMakeLists.txt
@ -911,12 +912,14 @@ llvm/tools/llvm-reduce/
llvm/tools/llvm-rtdyld/CMakeLists.txt
llvm/tools/llvm-rtdyld/LLVMBuild.txt
llvm/tools/llvm-shlib/
llvm/tools/llvm-size/
llvm/tools/llvm-size/CMakeLists.txt
llvm/tools/llvm-size/LLVMBuild.txt
llvm/tools/llvm-special-case-list-fuzzer/
llvm/tools/llvm-split/
llvm/tools/llvm-stress/CMakeLists.txt
llvm/tools/llvm-stress/LLVMBuild.txt
llvm/tools/llvm-strings/
llvm/tools/llvm-strings/CMakeLists.txt
llvm/tools/llvm-strings/LLVMBuild.txt
llvm/tools/llvm-symbolizer/CMakeLists.txt
llvm/tools/llvm-undname/
llvm/tools/llvm-xray/CMakeLists.txt

View File

@ -9677,7 +9677,8 @@ ABIArgInfo RISCVABIInfo::classifyArgumentType(QualType Ty, bool IsFixed,
uint64_t Size = getContext().getTypeSize(Ty);
// Pass floating point values via FPRs if possible.
if (IsFixed && Ty->isFloatingType() && FLen >= Size && ArgFPRsLeft) {
if (IsFixed && Ty->isFloatingType() && !Ty->isComplexType() &&
FLen >= Size && ArgFPRsLeft) {
ArgFPRsLeft--;
return ABIArgInfo::getDirect();
}

View File

@ -0,0 +1,31 @@
//===-- int_mul_impl.inc - Integer multiplication -------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Helpers used by __mulsi3, __muldi3.
//
//===----------------------------------------------------------------------===//
#if !defined(__riscv_mul)
.text
.align 2
.globl __mulxi3
.type __mulxi3, @function
__mulxi3:
mv a2, a0
mv a0, zero
.L1:
andi a3, a1, 1
beqz a3, .L2
add a0, a0, a2
.L2:
srli a1, a1, 1
slli a2, a2, 1
bnez a1, .L1
ret
#endif

View File

@ -0,0 +1,11 @@
//===--- muldi3.S - Integer multiplication routines -----------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#if __riscv_xlen == 64
#define __mulxi3 __muldi3
#include "int_mul_impl.inc"
#endif

View File

@ -1,4 +1,4 @@
//===--- mulsi3.S - Integer multiplication routines routines ---===//
//===--- mulsi3.S - Integer multiplication routines -----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@ -6,22 +6,7 @@
//
//===----------------------------------------------------------------------===//
#if !defined(__riscv_mul) && __riscv_xlen == 32
.text
.align 2
.globl __mulsi3
.type __mulsi3, @function
__mulsi3:
mv a2, a0
mv a0, zero
.L1:
andi a3, a1, 1
beqz a3, .L2
add a0, a0, a2
.L2:
srli a1, a1, 1
slli a2, a2, 1
bnez a1, .L1
ret
#if __riscv_xlen == 32
#define __mulxi3 __mulsi3
#include "int_mul_impl.inc"
#endif

View File

@ -359,7 +359,7 @@ struct _LIBCPP_TEMPLATE_VIS array<_Tp, 0>
#ifndef _LIBCPP_HAS_NO_DEDUCTION_GUIDES
template<class _Tp, class... _Args,
class = typename enable_if<(is_same_v<_Tp, _Args> && ...), void>::type
class = _EnableIf<__all<_IsSame<_Tp, _Args>::value...>::value>
>
array(_Tp, _Args...)
-> array<_Tp, 1 + sizeof...(_Args)>;

View File

@ -271,8 +271,20 @@ SDValue DAGTypeLegalizer::PromoteIntRes_AtomicCmpSwap(AtomicSDNode *N,
return Res.getValue(1);
}
SDValue Op2 = GetPromotedInteger(N->getOperand(2));
// Op2 is used for the comparison and thus must be extended according to the
// target's atomic operations. Op3 is merely stored and so can be left alone.
SDValue Op2 = N->getOperand(2);
SDValue Op3 = GetPromotedInteger(N->getOperand(3));
if (TLI.getTargetMachine().getTargetTriple().isRISCV()) {
// The comparison argument must be sign-extended for RISC-V. This is
// abstracted using a new TargetLowering hook in the main LLVM development
// branch, but handled here directly in order to fix the codegen bug for
// 10.x without breaking the libLLVM.so ABI.
Op2 = SExtPromotedInteger(Op2);
} else {
Op2 = GetPromotedInteger(Op2);
}
SDVTList VTs =
DAG.getVTList(Op2.getValueType(), N->getValueType(1), MVT::Other);
SDValue Res = DAG.getAtomicCmpSwap(

View File

@ -5490,9 +5490,20 @@ char TargetLowering::isNegatibleForFree(SDValue Op, SelectionDAG &DAG,
EVT VT = Op.getValueType();
const SDNodeFlags Flags = Op->getFlags();
const TargetOptions &Options = DAG.getTarget().Options;
if (!Op.hasOneUse() && !(Op.getOpcode() == ISD::FP_EXTEND &&
isFPExtFree(VT, Op.getOperand(0).getValueType())))
return 0;
if (!Op.hasOneUse()) {
bool IsFreeExtend = Op.getOpcode() == ISD::FP_EXTEND &&
isFPExtFree(VT, Op.getOperand(0).getValueType());
// If we already have the use of the negated floating constant, it is free
// to negate it even it has multiple uses.
bool IsFreeConstant =
Op.getOpcode() == ISD::ConstantFP &&
!getNegatedExpression(Op, DAG, LegalOperations, ForCodeSize)
.use_empty();
if (!IsFreeExtend && !IsFreeConstant)
return 0;
}
// Don't recurse exponentially.
if (Depth > SelectionDAG::MaxRecursionDepth)
@ -5687,14 +5698,7 @@ SDValue TargetLowering::getNegatedExpression(SDValue Op, SelectionDAG &DAG,
ForCodeSize, Depth + 1);
char V1 = isNegatibleForFree(Op.getOperand(1), DAG, LegalOperations,
ForCodeSize, Depth + 1);
// TODO: This is a hack. It is possible that costs have changed between now
// and the initial calls to isNegatibleForFree(). That is because we
// are rewriting the expression, and that may change the number of
// uses (and therefore the cost) of values. If the negation costs are
// equal, only negate this value if it is a constant. Otherwise, try
// operand 1. A better fix would eliminate uses as a cost factor or
// track the change in uses as we rewrite the expression.
if (V0 > V1 || (V0 == V1 && isa<ConstantFPSDNode>(Op.getOperand(0)))) {
if (V0 > V1) {
// fold (fneg (fma X, Y, Z)) -> (fma (fneg X), Y, (fneg Z))
SDValue Neg0 = getNegatedExpression(
Op.getOperand(0), DAG, LegalOperations, ForCodeSize, Depth + 1);

View File

@ -211,6 +211,24 @@ AArch64FrameLowering::getStackIDForScalableVectors() const {
return TargetStackID::SVEVector;
}
/// Returns the size of the fixed object area (allocated next to sp on entry)
/// On Win64 this may include a var args area and an UnwindHelp object for EH.
static unsigned getFixedObjectSize(const MachineFunction &MF,
const AArch64FunctionInfo *AFI, bool IsWin64,
bool IsFunclet) {
if (!IsWin64 || IsFunclet) {
// Only Win64 uses fixed objects, and then only for the function (not
// funclets)
return 0;
} else {
// Var args are stored here in the primary function.
const unsigned VarArgsArea = AFI->getVarArgsGPRSize();
// To support EH funclets we allocate an UnwindHelp object
const unsigned UnwindHelpObject = (MF.hasEHFunclets() ? 8 : 0);
return alignTo(VarArgsArea + UnwindHelpObject, 16);
}
}
/// Returns the size of the entire SVE stackframe (calleesaves + spills).
static StackOffset getSVEStackSize(const MachineFunction &MF) {
const AArch64FunctionInfo *AFI = MF.getInfo<AArch64FunctionInfo>();
@ -959,10 +977,7 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
bool IsWin64 =
Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv());
// Var args are accounted for in the containing function, so don't
// include them for funclets.
unsigned FixedObject = (IsWin64 && !IsFunclet) ?
alignTo(AFI->getVarArgsGPRSize(), 16) : 0;
unsigned FixedObject = getFixedObjectSize(MF, AFI, IsWin64, IsFunclet);
auto PrologueSaveSize = AFI->getCalleeSavedStackSize() + FixedObject;
// All of the remaining stack allocations are for locals.
@ -993,32 +1008,8 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
++MBBI;
}
// The code below is not applicable to funclets. We have emitted all the SEH
// opcodes that we needed to emit. The FP and BP belong to the containing
// function.
if (IsFunclet) {
if (NeedsWinCFI) {
HasWinCFI = true;
BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PrologEnd))
.setMIFlag(MachineInstr::FrameSetup);
}
// SEH funclets are passed the frame pointer in X1. If the parent
// function uses the base register, then the base register is used
// directly, and is not retrieved from X1.
if (F.hasPersonalityFn()) {
EHPersonality Per = classifyEHPersonality(F.getPersonalityFn());
if (isAsynchronousEHPersonality(Per)) {
BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::COPY), AArch64::FP)
.addReg(AArch64::X1).setMIFlag(MachineInstr::FrameSetup);
MBB.addLiveIn(AArch64::X1);
}
}
return;
}
if (HasFP) {
// For funclets the FP belongs to the containing function.
if (!IsFunclet && HasFP) {
// Only set up FP if we actually need to.
int64_t FPOffset = isTargetDarwin(MF) ? (AFI->getCalleeSavedStackSize() - 16) : 0;
@ -1161,7 +1152,9 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
// Allocate space for the rest of the frame.
if (NumBytes) {
const bool NeedsRealignment = RegInfo->needsStackRealignment(MF);
// Alignment is required for the parent frame, not the funclet
const bool NeedsRealignment =
!IsFunclet && RegInfo->needsStackRealignment(MF);
unsigned scratchSPReg = AArch64::SP;
if (NeedsRealignment) {
@ -1215,7 +1208,8 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
// FIXME: Clarify FrameSetup flags here.
// Note: Use emitFrameOffset() like above for FP if the FrameSetup flag is
// needed.
if (RegInfo->hasBasePointer(MF)) {
// For funclets the BP belongs to the containing function.
if (!IsFunclet && RegInfo->hasBasePointer(MF)) {
TII->copyPhysReg(MBB, MBBI, DL, RegInfo->getBaseRegister(), AArch64::SP,
false);
if (NeedsWinCFI) {
@ -1232,6 +1226,19 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
.setMIFlag(MachineInstr::FrameSetup);
}
// SEH funclets are passed the frame pointer in X1. If the parent
// function uses the base register, then the base register is used
// directly, and is not retrieved from X1.
if (IsFunclet && F.hasPersonalityFn()) {
EHPersonality Per = classifyEHPersonality(F.getPersonalityFn());
if (isAsynchronousEHPersonality(Per)) {
BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::COPY), AArch64::FP)
.addReg(AArch64::X1)
.setMIFlag(MachineInstr::FrameSetup);
MBB.addLiveIn(AArch64::X1);
}
}
if (needsFrameMoves) {
const DataLayout &TD = MF.getDataLayout();
const int StackGrowth = isTargetDarwin(MF)
@ -1450,10 +1457,7 @@ void AArch64FrameLowering::emitEpilogue(MachineFunction &MF,
bool IsWin64 =
Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv());
// Var args are accounted for in the containing function, so don't
// include them for funclets.
unsigned FixedObject =
(IsWin64 && !IsFunclet) ? alignTo(AFI->getVarArgsGPRSize(), 16) : 0;
unsigned FixedObject = getFixedObjectSize(MF, AFI, IsWin64, IsFunclet);
uint64_t AfterCSRPopSize = ArgumentPopSize;
auto PrologueSaveSize = AFI->getCalleeSavedStackSize() + FixedObject;
@ -1679,7 +1683,9 @@ static StackOffset getFPOffset(const MachineFunction &MF, int64_t ObjectOffset)
const auto &Subtarget = MF.getSubtarget<AArch64Subtarget>();
bool IsWin64 =
Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv());
unsigned FixedObject = IsWin64 ? alignTo(AFI->getVarArgsGPRSize(), 16) : 0;
unsigned FixedObject =
getFixedObjectSize(MF, AFI, IsWin64, /*IsFunclet=*/false);
unsigned FPAdjust = isTargetDarwin(MF)
? 16 : AFI->getCalleeSavedStackSize(MF.getFrameInfo());
return {ObjectOffset + FixedObject + FPAdjust, MVT::i8};
@ -2632,9 +2638,14 @@ void AArch64FrameLowering::processFunctionBeforeFrameFinalized(
++MBBI;
// Create an UnwindHelp object.
int UnwindHelpFI =
MFI.CreateStackObject(/*size*/8, /*alignment*/16, false);
// The UnwindHelp object is allocated at the start of the fixed object area
int64_t FixedObject =
getFixedObjectSize(MF, AFI, /*IsWin64*/ true, /*IsFunclet*/ false);
int UnwindHelpFI = MFI.CreateFixedObject(/*Size*/ 8,
/*SPOffset*/ -FixedObject,
/*IsImmutable=*/false);
EHInfo.UnwindHelpFrameIdx = UnwindHelpFI;
// We need to store -2 into the UnwindHelp object at the start of the
// function.
DebugLoc DL;
@ -2656,10 +2667,14 @@ int AArch64FrameLowering::getFrameIndexReferencePreferSP(
const MachineFunction &MF, int FI, unsigned &FrameReg,
bool IgnoreSPUpdates) const {
const MachineFrameInfo &MFI = MF.getFrameInfo();
LLVM_DEBUG(dbgs() << "Offset from the SP for " << FI << " is "
<< MFI.getObjectOffset(FI) << "\n");
FrameReg = AArch64::SP;
return MFI.getObjectOffset(FI);
if (IgnoreSPUpdates) {
LLVM_DEBUG(dbgs() << "Offset from the SP for " << FI << " is "
<< MFI.getObjectOffset(FI) << "\n");
FrameReg = AArch64::SP;
return MFI.getObjectOffset(FI);
}
return getFrameIndexReference(MF, FI, FrameReg);
}
/// The parent frame offset (aka dispFrame) is only used on X86_64 to retrieve

View File

@ -298,6 +298,11 @@ bool PPCAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
switch (ExtraCode[0]) {
default: return true; // Unknown modifier.
case 'L': // A memory reference to the upper word of a double word op.
O << getDataLayout().getPointerSize() << "(";
printOperand(MI, OpNo, O);
O << ")";
return false;
case 'y': // A memory reference for an X-form instruction
{
const char *RegName = "r0";
@ -309,7 +314,6 @@ bool PPCAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
}
case 'U': // Print 'u' for update form.
case 'X': // Print 'x' for indexed form.
{
// FIXME: Currently for PowerPC memory operands are always loaded
// into a register, so we never get an update or indexed form.
// This is bad even for offset forms, since even if we know we
@ -319,7 +323,6 @@ bool PPCAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
assert(MI->getOperand(OpNo).isReg());
return false;
}
}
}
assert(MI->getOperand(OpNo).isReg());

View File

@ -181,9 +181,9 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF,
adjustReg(MBB, MBBI, DL, FPReg, SPReg,
StackSize - RVFI->getVarArgsSaveSize(), MachineInstr::FrameSetup);
// Emit ".cfi_def_cfa $fp, 0"
// Emit ".cfi_def_cfa $fp, -RVFI->getVarArgsSaveSize()"
unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createDefCfa(
nullptr, RI->getDwarfRegNum(FPReg, true), 0));
nullptr, RI->getDwarfRegNum(FPReg, true), -RVFI->getVarArgsSaveSize()));
BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
}

View File

@ -197,6 +197,14 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
setTruncStoreAction(MVT::f64, MVT::f16, Expand);
}
if (Subtarget.is64Bit() &&
!(Subtarget.hasStdExtD() || Subtarget.hasStdExtF())) {
setOperationAction(ISD::FP_TO_UINT, MVT::i32, Custom);
setOperationAction(ISD::FP_TO_SINT, MVT::i32, Custom);
setOperationAction(ISD::STRICT_FP_TO_UINT, MVT::i32, Custom);
setOperationAction(ISD::STRICT_FP_TO_SINT, MVT::i32, Custom);
}
setOperationAction(ISD::GlobalAddress, XLenVT, Custom);
setOperationAction(ISD::BlockAddress, XLenVT, Custom);
setOperationAction(ISD::ConstantPool, XLenVT, Custom);
@ -876,6 +884,32 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N,
switch (N->getOpcode()) {
default:
llvm_unreachable("Don't know how to custom type legalize this operation!");
case ISD::STRICT_FP_TO_SINT:
case ISD::STRICT_FP_TO_UINT:
case ISD::FP_TO_SINT:
case ISD::FP_TO_UINT: {
bool IsStrict = N->isStrictFPOpcode();
assert(N->getValueType(0) == MVT::i32 && Subtarget.is64Bit() &&
"Unexpected custom legalisation");
SDValue Op0 = IsStrict ? N->getOperand(1) : N->getOperand(0);
RTLIB::Libcall LC;
if (N->getOpcode() == ISD::FP_TO_SINT ||
N->getOpcode() == ISD::STRICT_FP_TO_SINT)
LC = RTLIB::getFPTOSINT(Op0.getValueType(), N->getValueType(0));
else
LC = RTLIB::getFPTOUINT(Op0.getValueType(), N->getValueType(0));
MakeLibCallOptions CallOptions;
EVT OpVT = Op0.getValueType();
CallOptions.setTypeListBeforeSoften(OpVT, N->getValueType(0), true);
SDValue Chain = IsStrict ? N->getOperand(0) : SDValue();
SDValue Result;
std::tie(Result, Chain) =
makeLibCall(DAG, LC, N->getValueType(0), Op0, CallOptions, DL, Chain);
Results.push_back(Result);
if (IsStrict)
Results.push_back(Chain);
break;
}
case ISD::READCYCLECOUNTER: {
assert(!Subtarget.is64Bit() &&
"READCYCLECOUNTER only has custom type legalization on riscv32");

View File

@ -0,0 +1,3 @@
#include "DWPError.h"
using namespace llvm;
char DWPError::ID;

View File

@ -0,0 +1,23 @@
#ifndef TOOLS_LLVM_DWP_DWPERROR
#define TOOLS_LLVM_DWP_DWPERROR
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include <string>
namespace llvm {
class DWPError : public ErrorInfo<DWPError> {
public:
DWPError(std::string Info) : Info(std::move(Info)) {}
void log(raw_ostream &OS) const override { OS << Info; }
std::error_code convertToErrorCode() const override {
llvm_unreachable("Not implemented");
}
static char ID;
private:
std::string Info;
};
}
#endif

View File

@ -0,0 +1,56 @@
#ifndef TOOLS_LLVM_DWP_DWPSTRINGPOOL
#define TOOLS_LLVM_DWP_DWPSTRINGPOOL
#include "llvm/ADT/DenseMap.h"
#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCStreamer.h"
#include <cassert>
namespace llvm {
class DWPStringPool {
struct CStrDenseMapInfo {
static inline const char *getEmptyKey() {
return reinterpret_cast<const char *>(~static_cast<uintptr_t>(0));
}
static inline const char *getTombstoneKey() {
return reinterpret_cast<const char *>(~static_cast<uintptr_t>(1));
}
static unsigned getHashValue(const char *Val) {
assert(Val != getEmptyKey() && "Cannot hash the empty key!");
assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!");
return (unsigned)hash_value(StringRef(Val));
}
static bool isEqual(const char *LHS, const char *RHS) {
if (RHS == getEmptyKey())
return LHS == getEmptyKey();
if (RHS == getTombstoneKey())
return LHS == getTombstoneKey();
return strcmp(LHS, RHS) == 0;
}
};
MCStreamer &Out;
MCSection *Sec;
DenseMap<const char *, uint32_t, CStrDenseMapInfo> Pool;
uint32_t Offset = 0;
public:
DWPStringPool(MCStreamer &Out, MCSection *Sec) : Out(Out), Sec(Sec) {}
uint32_t getOffset(const char *Str, unsigned Length) {
assert(strlen(Str) + 1 == Length && "Ensure length hint is correct");
auto Pair = Pool.insert(std::make_pair(Str, Offset));
if (Pair.second) {
Out.SwitchSection(Sec);
Out.EmitBytes(StringRef(Str, Length));
Offset += Length;
}
return Pair.first->second;
}
};
}
#endif

View File

@ -0,0 +1,749 @@
//===-- llvm-dwp.cpp - Split DWARF merging tool for llvm ------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// A utility for merging DWARF 5 Split DWARF .dwo files into .dwp (DWARF
// package files).
//
//===----------------------------------------------------------------------===//
#include "DWPError.h"
#include "DWPStringPool.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCCodeEmitter.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCTargetOptionsCommandFlags.inc"
#include "llvm/Object/Decompressor.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace llvm::object;
cl::OptionCategory DwpCategory("Specific Options");
static cl::list<std::string> InputFiles(cl::Positional, cl::ZeroOrMore,
cl::desc("<input files>"),
cl::cat(DwpCategory));
static cl::list<std::string> ExecFilenames(
"e", cl::ZeroOrMore,
cl::desc("Specify the executable/library files to get the list of *.dwo from"),
cl::value_desc("filename"), cl::cat(DwpCategory));
static cl::opt<std::string> OutputFilename(cl::Required, "o",
cl::desc("Specify the output file."),
cl::value_desc("filename"),
cl::cat(DwpCategory));
static void writeStringsAndOffsets(MCStreamer &Out, DWPStringPool &Strings,
MCSection *StrOffsetSection,
StringRef CurStrSection,
StringRef CurStrOffsetSection) {
// Could possibly produce an error or warning if one of these was non-null but
// the other was null.
if (CurStrSection.empty() || CurStrOffsetSection.empty())
return;
DenseMap<uint64_t, uint32_t> OffsetRemapping;
DataExtractor Data(CurStrSection, true, 0);
uint64_t LocalOffset = 0;
uint64_t PrevOffset = 0;
while (const char *s = Data.getCStr(&LocalOffset)) {
OffsetRemapping[PrevOffset] =
Strings.getOffset(s, LocalOffset - PrevOffset);
PrevOffset = LocalOffset;
}
Data = DataExtractor(CurStrOffsetSection, true, 0);
Out.SwitchSection(StrOffsetSection);
uint64_t Offset = 0;
uint64_t Size = CurStrOffsetSection.size();
while (Offset < Size) {
auto OldOffset = Data.getU32(&Offset);
auto NewOffset = OffsetRemapping[OldOffset];
Out.EmitIntValue(NewOffset, 4);
}
}
static uint64_t getCUAbbrev(StringRef Abbrev, uint64_t AbbrCode) {
uint64_t CurCode;
uint64_t Offset = 0;
DataExtractor AbbrevData(Abbrev, true, 0);
while ((CurCode = AbbrevData.getULEB128(&Offset)) != AbbrCode) {
// Tag
AbbrevData.getULEB128(&Offset);
// DW_CHILDREN
AbbrevData.getU8(&Offset);
// Attributes
while (AbbrevData.getULEB128(&Offset) | AbbrevData.getULEB128(&Offset))
;
}
return Offset;
}
struct CompileUnitIdentifiers {
uint64_t Signature = 0;
const char *Name = "";
const char *DWOName = "";
};
static Expected<const char *>
getIndexedString(dwarf::Form Form, DataExtractor InfoData,
uint64_t &InfoOffset, StringRef StrOffsets, StringRef Str) {
if (Form == dwarf::DW_FORM_string)
return InfoData.getCStr(&InfoOffset);
if (Form != dwarf::DW_FORM_GNU_str_index)
return make_error<DWPError>(
"string field encoded without DW_FORM_string or DW_FORM_GNU_str_index");
auto StrIndex = InfoData.getULEB128(&InfoOffset);
DataExtractor StrOffsetsData(StrOffsets, true, 0);
uint64_t StrOffsetsOffset = 4 * StrIndex;
uint64_t StrOffset = StrOffsetsData.getU32(&StrOffsetsOffset);
DataExtractor StrData(Str, true, 0);
return StrData.getCStr(&StrOffset);
}
static Expected<CompileUnitIdentifiers> getCUIdentifiers(StringRef Abbrev,
StringRef Info,
StringRef StrOffsets,
StringRef Str) {
uint64_t Offset = 0;
DataExtractor InfoData(Info, true, 0);
dwarf::DwarfFormat Format = dwarf::DwarfFormat::DWARF32;
uint64_t Length = InfoData.getU32(&Offset);
// If the length is 0xffffffff, then this indictes that this is a DWARF 64
// stream and the length is actually encoded into a 64 bit value that follows.
if (Length == 0xffffffffU) {
Format = dwarf::DwarfFormat::DWARF64;
Length = InfoData.getU64(&Offset);
}
uint16_t Version = InfoData.getU16(&Offset);
InfoData.getU32(&Offset); // Abbrev offset (should be zero)
uint8_t AddrSize = InfoData.getU8(&Offset);
uint32_t AbbrCode = InfoData.getULEB128(&Offset);
DataExtractor AbbrevData(Abbrev, true, 0);
uint64_t AbbrevOffset = getCUAbbrev(Abbrev, AbbrCode);
auto Tag = static_cast<dwarf::Tag>(AbbrevData.getULEB128(&AbbrevOffset));
if (Tag != dwarf::DW_TAG_compile_unit)
return make_error<DWPError>("top level DIE is not a compile unit");
// DW_CHILDREN
AbbrevData.getU8(&AbbrevOffset);
uint32_t Name;
dwarf::Form Form;
CompileUnitIdentifiers ID;
Optional<uint64_t> Signature = None;
while ((Name = AbbrevData.getULEB128(&AbbrevOffset)) |
(Form = static_cast<dwarf::Form>(AbbrevData.getULEB128(&AbbrevOffset))) &&
(Name != 0 || Form != 0)) {
switch (Name) {
case dwarf::DW_AT_name: {
Expected<const char *> EName =
getIndexedString(Form, InfoData, Offset, StrOffsets, Str);
if (!EName)
return EName.takeError();
ID.Name = *EName;
break;
}
case dwarf::DW_AT_GNU_dwo_name: {
Expected<const char *> EName =
getIndexedString(Form, InfoData, Offset, StrOffsets, Str);
if (!EName)
return EName.takeError();
ID.DWOName = *EName;
break;
}
case dwarf::DW_AT_GNU_dwo_id:
Signature = InfoData.getU64(&Offset);
break;
default:
DWARFFormValue::skipValue(Form, InfoData, &Offset,
dwarf::FormParams({Version, AddrSize, Format}));
}
}
if (!Signature)
return make_error<DWPError>("compile unit missing dwo_id");
ID.Signature = *Signature;
return ID;
}
struct UnitIndexEntry {
DWARFUnitIndex::Entry::SectionContribution Contributions[8];
std::string Name;
std::string DWOName;
StringRef DWPName;
};
static StringRef getSubsection(StringRef Section,
const DWARFUnitIndex::Entry &Entry,
DWARFSectionKind Kind) {
const auto *Off = Entry.getOffset(Kind);
if (!Off)
return StringRef();
return Section.substr(Off->Offset, Off->Length);
}
static void addAllTypesFromDWP(
MCStreamer &Out, MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries,
const DWARFUnitIndex &TUIndex, MCSection *OutputTypes, StringRef Types,
const UnitIndexEntry &TUEntry, uint32_t &TypesOffset) {
Out.SwitchSection(OutputTypes);
for (const DWARFUnitIndex::Entry &E : TUIndex.getRows()) {
auto *I = E.getOffsets();
if (!I)
continue;
auto P = TypeIndexEntries.insert(std::make_pair(E.getSignature(), TUEntry));
if (!P.second)
continue;
auto &Entry = P.first->second;
// Zero out the debug_info contribution
Entry.Contributions[0] = {};
for (auto Kind : TUIndex.getColumnKinds()) {
auto &C = Entry.Contributions[Kind - DW_SECT_INFO];
C.Offset += I->Offset;
C.Length = I->Length;
++I;
}
auto &C = Entry.Contributions[DW_SECT_TYPES - DW_SECT_INFO];
Out.EmitBytes(Types.substr(
C.Offset - TUEntry.Contributions[DW_SECT_TYPES - DW_SECT_INFO].Offset,
C.Length));
C.Offset = TypesOffset;
TypesOffset += C.Length;
}
}
static void addAllTypes(MCStreamer &Out,
MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries,
MCSection *OutputTypes,
const std::vector<StringRef> &TypesSections,
const UnitIndexEntry &CUEntry, uint32_t &TypesOffset) {
for (StringRef Types : TypesSections) {
Out.SwitchSection(OutputTypes);
uint64_t Offset = 0;
DataExtractor Data(Types, true, 0);
while (Data.isValidOffset(Offset)) {
UnitIndexEntry Entry = CUEntry;
// Zero out the debug_info contribution
Entry.Contributions[0] = {};
auto &C = Entry.Contributions[DW_SECT_TYPES - DW_SECT_INFO];
C.Offset = TypesOffset;
auto PrevOffset = Offset;
// Length of the unit, including the 4 byte length field.
C.Length = Data.getU32(&Offset) + 4;
Data.getU16(&Offset); // Version
Data.getU32(&Offset); // Abbrev offset
Data.getU8(&Offset); // Address size
auto Signature = Data.getU64(&Offset);
Offset = PrevOffset + C.Length;
auto P = TypeIndexEntries.insert(std::make_pair(Signature, Entry));
if (!P.second)
continue;
Out.EmitBytes(Types.substr(PrevOffset, C.Length));
TypesOffset += C.Length;
}
}
}
static void
writeIndexTable(MCStreamer &Out, ArrayRef<unsigned> ContributionOffsets,
const MapVector<uint64_t, UnitIndexEntry> &IndexEntries,
uint32_t DWARFUnitIndex::Entry::SectionContribution::*Field) {
for (const auto &E : IndexEntries)
for (size_t i = 0; i != array_lengthof(E.second.Contributions); ++i)
if (ContributionOffsets[i])
Out.EmitIntValue(E.second.Contributions[i].*Field, 4);
}
static void
writeIndex(MCStreamer &Out, MCSection *Section,
ArrayRef<unsigned> ContributionOffsets,
const MapVector<uint64_t, UnitIndexEntry> &IndexEntries) {
if (IndexEntries.empty())
return;
unsigned Columns = 0;
for (auto &C : ContributionOffsets)
if (C)
++Columns;
std::vector<unsigned> Buckets(NextPowerOf2(3 * IndexEntries.size() / 2));
uint64_t Mask = Buckets.size() - 1;
size_t i = 0;
for (const auto &P : IndexEntries) {
auto S = P.first;
auto H = S & Mask;
auto HP = ((S >> 32) & Mask) | 1;
while (Buckets[H]) {
assert(S != IndexEntries.begin()[Buckets[H] - 1].first &&
"Duplicate unit");
H = (H + HP) & Mask;
}
Buckets[H] = i + 1;
++i;
}
Out.SwitchSection(Section);
Out.EmitIntValue(2, 4); // Version
Out.EmitIntValue(Columns, 4); // Columns
Out.EmitIntValue(IndexEntries.size(), 4); // Num Units
Out.EmitIntValue(Buckets.size(), 4); // Num Buckets
// Write the signatures.
for (const auto &I : Buckets)
Out.EmitIntValue(I ? IndexEntries.begin()[I - 1].first : 0, 8);
// Write the indexes.
for (const auto &I : Buckets)
Out.EmitIntValue(I, 4);
// Write the column headers (which sections will appear in the table)
for (size_t i = 0; i != ContributionOffsets.size(); ++i)
if (ContributionOffsets[i])
Out.EmitIntValue(i + DW_SECT_INFO, 4);
// Write the offsets.
writeIndexTable(Out, ContributionOffsets, IndexEntries,
&DWARFUnitIndex::Entry::SectionContribution::Offset);
// Write the lengths.
writeIndexTable(Out, ContributionOffsets, IndexEntries,
&DWARFUnitIndex::Entry::SectionContribution::Length);
}
std::string buildDWODescription(StringRef Name, StringRef DWPName, StringRef DWOName) {
std::string Text = "\'";
Text += Name;
Text += '\'';
if (!DWPName.empty()) {
Text += " (from ";
if (!DWOName.empty()) {
Text += '\'';
Text += DWOName;
Text += "' in ";
}
Text += '\'';
Text += DWPName;
Text += "')";
}
return Text;
}
static Error createError(StringRef Name, Error E) {
return make_error<DWPError>(
("failure while decompressing compressed section: '" + Name + "', " +
llvm::toString(std::move(E)))
.str());
}
static Error
handleCompressedSection(std::deque<SmallString<32>> &UncompressedSections,
StringRef &Name, StringRef &Contents) {
if (!Decompressor::isGnuStyle(Name))
return Error::success();
Expected<Decompressor> Dec =
Decompressor::create(Name, Contents, false /*IsLE*/, false /*Is64Bit*/);
if (!Dec)
return createError(Name, Dec.takeError());
UncompressedSections.emplace_back();
if (Error E = Dec->resizeAndDecompress(UncompressedSections.back()))
return createError(Name, std::move(E));
Name = Name.substr(2); // Drop ".z"
Contents = UncompressedSections.back();
return Error::success();
}
static Error handleSection(
const StringMap<std::pair<MCSection *, DWARFSectionKind>> &KnownSections,
const MCSection *StrSection, const MCSection *StrOffsetSection,
const MCSection *TypesSection, const MCSection *CUIndexSection,
const MCSection *TUIndexSection, const SectionRef &Section, MCStreamer &Out,
std::deque<SmallString<32>> &UncompressedSections,
uint32_t (&ContributionOffsets)[8], UnitIndexEntry &CurEntry,
StringRef &CurStrSection, StringRef &CurStrOffsetSection,
std::vector<StringRef> &CurTypesSection, StringRef &InfoSection,
StringRef &AbbrevSection, StringRef &CurCUIndexSection,
StringRef &CurTUIndexSection) {
if (Section.isBSS())
return Error::success();
if (Section.isVirtual())
return Error::success();
Expected<StringRef> NameOrErr = Section.getName();
if (!NameOrErr)
return NameOrErr.takeError();
StringRef Name = *NameOrErr;
Expected<StringRef> ContentsOrErr = Section.getContents();
if (!ContentsOrErr)
return ContentsOrErr.takeError();
StringRef Contents = *ContentsOrErr;
if (auto Err = handleCompressedSection(UncompressedSections, Name, Contents))
return Err;
Name = Name.substr(Name.find_first_not_of("._"));
auto SectionPair = KnownSections.find(Name);
if (SectionPair == KnownSections.end())
return Error::success();
if (DWARFSectionKind Kind = SectionPair->second.second) {
auto Index = Kind - DW_SECT_INFO;
if (Kind != DW_SECT_TYPES) {
CurEntry.Contributions[Index].Offset = ContributionOffsets[Index];
ContributionOffsets[Index] +=
(CurEntry.Contributions[Index].Length = Contents.size());
}
switch (Kind) {
case DW_SECT_INFO:
InfoSection = Contents;
break;
case DW_SECT_ABBREV:
AbbrevSection = Contents;
break;
default:
break;
}
}
MCSection *OutSection = SectionPair->second.first;
if (OutSection == StrOffsetSection)
CurStrOffsetSection = Contents;
else if (OutSection == StrSection)
CurStrSection = Contents;
else if (OutSection == TypesSection)
CurTypesSection.push_back(Contents);
else if (OutSection == CUIndexSection)
CurCUIndexSection = Contents;
else if (OutSection == TUIndexSection)
CurTUIndexSection = Contents;
else {
Out.SwitchSection(OutSection);
Out.EmitBytes(Contents);
}
return Error::success();
}
static Error
buildDuplicateError(const std::pair<uint64_t, UnitIndexEntry> &PrevE,
const CompileUnitIdentifiers &ID, StringRef DWPName) {
return make_error<DWPError>(
std::string("Duplicate DWO ID (") + utohexstr(PrevE.first) + ") in " +
buildDWODescription(PrevE.second.Name, PrevE.second.DWPName,
PrevE.second.DWOName) +
" and " + buildDWODescription(ID.Name, DWPName, ID.DWOName));
}
static Expected<SmallVector<std::string, 16>>
getDWOFilenames(StringRef ExecFilename) {
auto ErrOrObj = object::ObjectFile::createObjectFile(ExecFilename);
if (!ErrOrObj)
return ErrOrObj.takeError();
const ObjectFile &Obj = *ErrOrObj.get().getBinary();
std::unique_ptr<DWARFContext> DWARFCtx = DWARFContext::create(Obj);
SmallVector<std::string, 16> DWOPaths;
for (const auto &CU : DWARFCtx->compile_units()) {
const DWARFDie &Die = CU->getUnitDIE();
std::string DWOName = dwarf::toString(
Die.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), "");
if (DWOName.empty())
continue;
std::string DWOCompDir =
dwarf::toString(Die.find(dwarf::DW_AT_comp_dir), "");
if (!DWOCompDir.empty()) {
SmallString<16> DWOPath;
sys::path::append(DWOPath, DWOCompDir, DWOName);
DWOPaths.emplace_back(DWOPath.data(), DWOPath.size());
} else {
DWOPaths.push_back(std::move(DWOName));
}
}
return std::move(DWOPaths);
}
static Error write(MCStreamer &Out, ArrayRef<std::string> Inputs) {
const auto &MCOFI = *Out.getContext().getObjectFileInfo();
MCSection *const StrSection = MCOFI.getDwarfStrDWOSection();
MCSection *const StrOffsetSection = MCOFI.getDwarfStrOffDWOSection();
MCSection *const TypesSection = MCOFI.getDwarfTypesDWOSection();
MCSection *const CUIndexSection = MCOFI.getDwarfCUIndexSection();
MCSection *const TUIndexSection = MCOFI.getDwarfTUIndexSection();
const StringMap<std::pair<MCSection *, DWARFSectionKind>> KnownSections = {
{"debug_info.dwo", {MCOFI.getDwarfInfoDWOSection(), DW_SECT_INFO}},
{"debug_types.dwo", {MCOFI.getDwarfTypesDWOSection(), DW_SECT_TYPES}},
{"debug_str_offsets.dwo", {StrOffsetSection, DW_SECT_STR_OFFSETS}},
{"debug_str.dwo", {StrSection, static_cast<DWARFSectionKind>(0)}},
{"debug_loc.dwo", {MCOFI.getDwarfLocDWOSection(), DW_SECT_LOC}},
{"debug_line.dwo", {MCOFI.getDwarfLineDWOSection(), DW_SECT_LINE}},
{"debug_abbrev.dwo", {MCOFI.getDwarfAbbrevDWOSection(), DW_SECT_ABBREV}},
{"debug_cu_index", {CUIndexSection, static_cast<DWARFSectionKind>(0)}},
{"debug_tu_index", {TUIndexSection, static_cast<DWARFSectionKind>(0)}}};
MapVector<uint64_t, UnitIndexEntry> IndexEntries;
MapVector<uint64_t, UnitIndexEntry> TypeIndexEntries;
uint32_t ContributionOffsets[8] = {};
DWPStringPool Strings(Out, StrSection);
SmallVector<OwningBinary<object::ObjectFile>, 128> Objects;
Objects.reserve(Inputs.size());
std::deque<SmallString<32>> UncompressedSections;
for (const auto &Input : Inputs) {
auto ErrOrObj = object::ObjectFile::createObjectFile(Input);
if (!ErrOrObj)
return ErrOrObj.takeError();
auto &Obj = *ErrOrObj->getBinary();
Objects.push_back(std::move(*ErrOrObj));
UnitIndexEntry CurEntry = {};
StringRef CurStrSection;
StringRef CurStrOffsetSection;
std::vector<StringRef> CurTypesSection;
StringRef InfoSection;
StringRef AbbrevSection;
StringRef CurCUIndexSection;
StringRef CurTUIndexSection;
for (const auto &Section : Obj.sections())
if (auto Err = handleSection(
KnownSections, StrSection, StrOffsetSection, TypesSection,
CUIndexSection, TUIndexSection, Section, Out,
UncompressedSections, ContributionOffsets, CurEntry,
CurStrSection, CurStrOffsetSection, CurTypesSection, InfoSection,
AbbrevSection, CurCUIndexSection, CurTUIndexSection))
return Err;
if (InfoSection.empty())
continue;
writeStringsAndOffsets(Out, Strings, StrOffsetSection, CurStrSection,
CurStrOffsetSection);
if (CurCUIndexSection.empty()) {
Expected<CompileUnitIdentifiers> EID = getCUIdentifiers(
AbbrevSection, InfoSection, CurStrOffsetSection, CurStrSection);
if (!EID)
return createFileError(Input, EID.takeError());
const auto &ID = *EID;
auto P = IndexEntries.insert(std::make_pair(ID.Signature, CurEntry));
if (!P.second)
return buildDuplicateError(*P.first, ID, "");
P.first->second.Name = ID.Name;
P.first->second.DWOName = ID.DWOName;
addAllTypes(Out, TypeIndexEntries, TypesSection, CurTypesSection,
CurEntry, ContributionOffsets[DW_SECT_TYPES - DW_SECT_INFO]);
continue;
}
DWARFUnitIndex CUIndex(DW_SECT_INFO);
DataExtractor CUIndexData(CurCUIndexSection, Obj.isLittleEndian(), 0);
if (!CUIndex.parse(CUIndexData))
return make_error<DWPError>("Failed to parse cu_index");
for (const DWARFUnitIndex::Entry &E : CUIndex.getRows()) {
auto *I = E.getOffsets();
if (!I)
continue;
auto P = IndexEntries.insert(std::make_pair(E.getSignature(), CurEntry));
Expected<CompileUnitIdentifiers> EID = getCUIdentifiers(
getSubsection(AbbrevSection, E, DW_SECT_ABBREV),
getSubsection(InfoSection, E, DW_SECT_INFO),
getSubsection(CurStrOffsetSection, E, DW_SECT_STR_OFFSETS),
CurStrSection);
if (!EID)
return createFileError(Input, EID.takeError());
const auto &ID = *EID;
if (!P.second)
return buildDuplicateError(*P.first, ID, Input);
auto &NewEntry = P.first->second;
NewEntry.Name = ID.Name;
NewEntry.DWOName = ID.DWOName;
NewEntry.DWPName = Input;
for (auto Kind : CUIndex.getColumnKinds()) {
auto &C = NewEntry.Contributions[Kind - DW_SECT_INFO];
C.Offset += I->Offset;
C.Length = I->Length;
++I;
}
}
if (!CurTypesSection.empty()) {
if (CurTypesSection.size() != 1)
return make_error<DWPError>("multiple type unit sections in .dwp file");
DWARFUnitIndex TUIndex(DW_SECT_TYPES);
DataExtractor TUIndexData(CurTUIndexSection, Obj.isLittleEndian(), 0);
if (!TUIndex.parse(TUIndexData))
return make_error<DWPError>("Failed to parse tu_index");
addAllTypesFromDWP(Out, TypeIndexEntries, TUIndex, TypesSection,
CurTypesSection.front(), CurEntry,
ContributionOffsets[DW_SECT_TYPES - DW_SECT_INFO]);
}
}
// Lie about there being no info contributions so the TU index only includes
// the type unit contribution
ContributionOffsets[0] = 0;
writeIndex(Out, MCOFI.getDwarfTUIndexSection(), ContributionOffsets,
TypeIndexEntries);
// Lie about the type contribution
ContributionOffsets[DW_SECT_TYPES - DW_SECT_INFO] = 0;
// Unlie about the info contribution
ContributionOffsets[0] = 1;
writeIndex(Out, MCOFI.getDwarfCUIndexSection(), ContributionOffsets,
IndexEntries);
return Error::success();
}
static int error(const Twine &Error, const Twine &Context) {
errs() << Twine("while processing ") + Context + ":\n";
errs() << Twine("error: ") + Error + "\n";
return 1;
}
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
cl::ParseCommandLineOptions(argc, argv, "merge split dwarf (.dwo) files\n");
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllTargets();
llvm::InitializeAllAsmPrinters();
std::string ErrorStr;
StringRef Context = "dwarf streamer init";
Triple TheTriple("x86_64-linux-gnu");
// Get the target.
const Target *TheTarget =
TargetRegistry::lookupTarget("", TheTriple, ErrorStr);
if (!TheTarget)
return error(ErrorStr, Context);
std::string TripleName = TheTriple.getTriple();
// Create all the MC Objects.
std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
if (!MRI)
return error(Twine("no register info for target ") + TripleName, Context);
MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags();
std::unique_ptr<MCAsmInfo> MAI(
TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
if (!MAI)
return error("no asm info for target " + TripleName, Context);
MCObjectFileInfo MOFI;
MCContext MC(MAI.get(), MRI.get(), &MOFI);
MOFI.InitMCObjectFileInfo(TheTriple, /*PIC*/ false, MC);
std::unique_ptr<MCSubtargetInfo> MSTI(
TheTarget->createMCSubtargetInfo(TripleName, "", ""));
if (!MSTI)
return error("no subtarget info for target " + TripleName, Context);
MCTargetOptions Options;
auto MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, Options);
if (!MAB)
return error("no asm backend for target " + TripleName, Context);
std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo());
if (!MII)
return error("no instr info info for target " + TripleName, Context);
MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, MC);
if (!MCE)
return error("no code emitter for target " + TripleName, Context);
// Create the output file.
std::error_code EC;
ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_None);
Optional<buffer_ostream> BOS;
raw_pwrite_stream *OS;
if (EC)
return error(Twine(OutputFilename) + ": " + EC.message(), Context);
if (OutFile.os().supportsSeeking()) {
OS = &OutFile.os();
} else {
BOS.emplace(OutFile.os());
OS = BOS.getPointer();
}
std::unique_ptr<MCStreamer> MS(TheTarget->createMCObjectStreamer(
TheTriple, MC, std::unique_ptr<MCAsmBackend>(MAB),
MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(MCE), *MSTI,
MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible,
/*DWARFMustBeAtTheEnd*/ false));
if (!MS)
return error("no object streamer for target " + TripleName, Context);
std::vector<std::string> DWOFilenames = InputFiles;
for (const auto &ExecFilename : ExecFilenames) {
auto DWOs = getDWOFilenames(ExecFilename);
if (!DWOs) {
logAllUnhandledErrors(DWOs.takeError(), WithColor::error());
return 1;
}
DWOFilenames.insert(DWOFilenames.end(),
std::make_move_iterator(DWOs->begin()),
std::make_move_iterator(DWOs->end()));
}
if (auto Err = write(*MS, DWOFilenames)) {
logAllUnhandledErrors(std::move(Err), WithColor::error());
return 1;
}
MS->Finish();
OutFile.keep();
return 0;
}

View File

@ -0,0 +1,894 @@
//===-- llvm-size.cpp - Print the size of each object section ---*- 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
//
//===----------------------------------------------------------------------===//
//
// This program is a utility that works like traditional Unix "size",
// that is, it prints out the size of each section, and the total size of all
// sections.
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/APInt.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/MachO.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <string>
#include <system_error>
using namespace llvm;
using namespace object;
cl::OptionCategory SizeCat("llvm-size Options");
enum OutputFormatTy { berkeley, sysv, darwin };
static cl::opt<OutputFormatTy>
OutputFormat("format", cl::desc("Specify output format"),
cl::values(clEnumVal(sysv, "System V format"),
clEnumVal(berkeley, "Berkeley format"),
clEnumVal(darwin, "Darwin -m format")),
cl::init(berkeley), cl::cat(SizeCat));
static cl::opt<OutputFormatTy>
OutputFormatShort(cl::desc("Specify output format"),
cl::values(clEnumValN(sysv, "A", "System V format"),
clEnumValN(berkeley, "B", "Berkeley format"),
clEnumValN(darwin, "m", "Darwin -m format")),
cl::init(berkeley), cl::cat(SizeCat));
static bool BerkeleyHeaderPrinted = false;
static bool MoreThanOneFile = false;
static uint64_t TotalObjectText = 0;
static uint64_t TotalObjectData = 0;
static uint64_t TotalObjectBss = 0;
static uint64_t TotalObjectTotal = 0;
cl::opt<bool>
DarwinLongFormat("l",
cl::desc("When format is darwin, use long format "
"to include addresses and offsets."),
cl::cat(SizeCat));
cl::opt<bool>
ELFCommons("common",
cl::desc("Print common symbols in the ELF file. When using "
"Berkeley format, this is added to bss."),
cl::init(false), cl::cat(SizeCat));
static cl::list<std::string>
ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"),
cl::ZeroOrMore, cl::cat(SizeCat));
static bool ArchAll = false;
enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 };
static cl::opt<RadixTy> Radix(
"radix", cl::desc("Print size in radix"), cl::init(decimal),
cl::values(clEnumValN(octal, "8", "Print size in octal"),
clEnumValN(decimal, "10", "Print size in decimal"),
clEnumValN(hexadecimal, "16", "Print size in hexadecimal")),
cl::cat(SizeCat));
static cl::opt<RadixTy> RadixShort(
cl::desc("Print size in radix:"),
cl::values(clEnumValN(octal, "o", "Print size in octal"),
clEnumValN(decimal, "d", "Print size in decimal"),
clEnumValN(hexadecimal, "x", "Print size in hexadecimal")),
cl::init(decimal), cl::cat(SizeCat));
static cl::opt<bool>
TotalSizes("totals",
cl::desc("Print totals of all objects - Berkeley format only"),
cl::init(false), cl::cat(SizeCat));
static cl::alias TotalSizesShort("t", cl::desc("Short for --totals"),
cl::aliasopt(TotalSizes));
static cl::list<std::string>
InputFilenames(cl::Positional, cl::desc("<input files>"), cl::ZeroOrMore);
static cl::extrahelp
HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
static bool HadError = false;
static std::string ToolName;
static void error(const Twine &Message, StringRef File) {
HadError = true;
WithColor::error(errs(), ToolName) << "'" << File << "': " << Message << "\n";
}
// This version of error() prints the archive name and member name, for example:
// "libx.a(foo.o)" after the ToolName before the error message. It sets
// HadError but returns allowing the code to move on to other archive members.
static void error(llvm::Error E, StringRef FileName, const Archive::Child &C,
StringRef ArchitectureName = StringRef()) {
HadError = true;
WithColor::error(errs(), ToolName) << "'" << FileName << "'";
Expected<StringRef> NameOrErr = C.getName();
// TODO: if we have a error getting the name then it would be nice to print
// the index of which archive member this is and or its offset in the
// archive instead of "???" as the name.
if (!NameOrErr) {
consumeError(NameOrErr.takeError());
errs() << "(" << "???" << ")";
} else
errs() << "(" << NameOrErr.get() << ")";
if (!ArchitectureName.empty())
errs() << " (for architecture " << ArchitectureName << ") ";
std::string Buf;
raw_string_ostream OS(Buf);
logAllUnhandledErrors(std::move(E), OS);
OS.flush();
errs() << ": " << Buf << "\n";
}
// This version of error() prints the file name and which architecture slice it // is from, for example: "foo.o (for architecture i386)" after the ToolName
// before the error message. It sets HadError but returns allowing the code to
// move on to other architecture slices.
static void error(llvm::Error E, StringRef FileName,
StringRef ArchitectureName = StringRef()) {
HadError = true;
WithColor::error(errs(), ToolName) << "'" << FileName << "'";
if (!ArchitectureName.empty())
errs() << " (for architecture " << ArchitectureName << ") ";
std::string Buf;
raw_string_ostream OS(Buf);
logAllUnhandledErrors(std::move(E), OS);
OS.flush();
errs() << ": " << Buf << "\n";
}
/// Get the length of the string that represents @p num in Radix including the
/// leading 0x or 0 for hexadecimal and octal respectively.
static size_t getNumLengthAsString(uint64_t num) {
APInt conv(64, num);
SmallString<32> result;
conv.toString(result, Radix, false, true);
return result.size();
}
/// Return the printing format for the Radix.
static const char *getRadixFmt() {
switch (Radix) {
case octal:
return PRIo64;
case decimal:
return PRIu64;
case hexadecimal:
return PRIx64;
}
return nullptr;
}
/// Remove unneeded ELF sections from calculation
static bool considerForSize(ObjectFile *Obj, SectionRef Section) {
if (!Obj->isELF())
return true;
switch (static_cast<ELFSectionRef>(Section).getType()) {
case ELF::SHT_NULL:
case ELF::SHT_SYMTAB:
case ELF::SHT_STRTAB:
case ELF::SHT_REL:
case ELF::SHT_RELA:
return false;
}
return true;
}
/// Total size of all ELF common symbols
static uint64_t getCommonSize(ObjectFile *Obj) {
uint64_t TotalCommons = 0;
for (auto &Sym : Obj->symbols())
if (Obj->getSymbolFlags(Sym.getRawDataRefImpl()) & SymbolRef::SF_Common)
TotalCommons += Obj->getCommonSymbolSize(Sym.getRawDataRefImpl());
return TotalCommons;
}
/// Print the size of each Mach-O segment and section in @p MachO.
///
/// This is when used when @c OutputFormat is darwin and produces the same
/// output as darwin's size(1) -m output.
static void printDarwinSectionSizes(MachOObjectFile *MachO) {
std::string fmtbuf;
raw_string_ostream fmt(fmtbuf);
const char *radix_fmt = getRadixFmt();
if (Radix == hexadecimal)
fmt << "0x";
fmt << "%" << radix_fmt;
uint32_t Filetype = MachO->getHeader().filetype;
uint64_t total = 0;
for (const auto &Load : MachO->load_commands()) {
if (Load.C.cmd == MachO::LC_SEGMENT_64) {
MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load);
outs() << "Segment " << Seg.segname << ": "
<< format(fmt.str().c_str(), Seg.vmsize);
if (DarwinLongFormat)
outs() << " (vmaddr 0x" << format("%" PRIx64, Seg.vmaddr) << " fileoff "
<< Seg.fileoff << ")";
outs() << "\n";
total += Seg.vmsize;
uint64_t sec_total = 0;
for (unsigned J = 0; J < Seg.nsects; ++J) {
MachO::section_64 Sec = MachO->getSection64(Load, J);
if (Filetype == MachO::MH_OBJECT)
outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", "
<< format("%.16s", &Sec.sectname) << "): ";
else
outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": ";
outs() << format(fmt.str().c_str(), Sec.size);
if (DarwinLongFormat)
outs() << " (addr 0x" << format("%" PRIx64, Sec.addr) << " offset "
<< Sec.offset << ")";
outs() << "\n";
sec_total += Sec.size;
}
if (Seg.nsects != 0)
outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n";
} else if (Load.C.cmd == MachO::LC_SEGMENT) {
MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load);
uint64_t Seg_vmsize = Seg.vmsize;
outs() << "Segment " << Seg.segname << ": "
<< format(fmt.str().c_str(), Seg_vmsize);
if (DarwinLongFormat)
outs() << " (vmaddr 0x" << format("%" PRIx32, Seg.vmaddr) << " fileoff "
<< Seg.fileoff << ")";
outs() << "\n";
total += Seg.vmsize;
uint64_t sec_total = 0;
for (unsigned J = 0; J < Seg.nsects; ++J) {
MachO::section Sec = MachO->getSection(Load, J);
if (Filetype == MachO::MH_OBJECT)
outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", "
<< format("%.16s", &Sec.sectname) << "): ";
else
outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": ";
uint64_t Sec_size = Sec.size;
outs() << format(fmt.str().c_str(), Sec_size);
if (DarwinLongFormat)
outs() << " (addr 0x" << format("%" PRIx32, Sec.addr) << " offset "
<< Sec.offset << ")";
outs() << "\n";
sec_total += Sec.size;
}
if (Seg.nsects != 0)
outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n";
}
}
outs() << "total " << format(fmt.str().c_str(), total) << "\n";
}
/// Print the summary sizes of the standard Mach-O segments in @p MachO.
///
/// This is when used when @c OutputFormat is berkeley with a Mach-O file and
/// produces the same output as darwin's size(1) default output.
static void printDarwinSegmentSizes(MachOObjectFile *MachO) {
uint64_t total_text = 0;
uint64_t total_data = 0;
uint64_t total_objc = 0;
uint64_t total_others = 0;
for (const auto &Load : MachO->load_commands()) {
if (Load.C.cmd == MachO::LC_SEGMENT_64) {
MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load);
if (MachO->getHeader().filetype == MachO::MH_OBJECT) {
for (unsigned J = 0; J < Seg.nsects; ++J) {
MachO::section_64 Sec = MachO->getSection64(Load, J);
StringRef SegmentName = StringRef(Sec.segname);
if (SegmentName == "__TEXT")
total_text += Sec.size;
else if (SegmentName == "__DATA")
total_data += Sec.size;
else if (SegmentName == "__OBJC")
total_objc += Sec.size;
else
total_others += Sec.size;
}
} else {
StringRef SegmentName = StringRef(Seg.segname);
if (SegmentName == "__TEXT")
total_text += Seg.vmsize;
else if (SegmentName == "__DATA")
total_data += Seg.vmsize;
else if (SegmentName == "__OBJC")
total_objc += Seg.vmsize;
else
total_others += Seg.vmsize;
}
} else if (Load.C.cmd == MachO::LC_SEGMENT) {
MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load);
if (MachO->getHeader().filetype == MachO::MH_OBJECT) {
for (unsigned J = 0; J < Seg.nsects; ++J) {
MachO::section Sec = MachO->getSection(Load, J);
StringRef SegmentName = StringRef(Sec.segname);
if (SegmentName == "__TEXT")
total_text += Sec.size;
else if (SegmentName == "__DATA")
total_data += Sec.size;
else if (SegmentName == "__OBJC")
total_objc += Sec.size;
else
total_others += Sec.size;
}
} else {
StringRef SegmentName = StringRef(Seg.segname);
if (SegmentName == "__TEXT")
total_text += Seg.vmsize;
else if (SegmentName == "__DATA")
total_data += Seg.vmsize;
else if (SegmentName == "__OBJC")
total_objc += Seg.vmsize;
else
total_others += Seg.vmsize;
}
}
}
uint64_t total = total_text + total_data + total_objc + total_others;
if (!BerkeleyHeaderPrinted) {
outs() << "__TEXT\t__DATA\t__OBJC\tothers\tdec\thex\n";
BerkeleyHeaderPrinted = true;
}
outs() << total_text << "\t" << total_data << "\t" << total_objc << "\t"
<< total_others << "\t" << total << "\t" << format("%" PRIx64, total)
<< "\t";
}
/// Print the size of each section in @p Obj.
///
/// The format used is determined by @c OutputFormat and @c Radix.
static void printObjectSectionSizes(ObjectFile *Obj) {
uint64_t total = 0;
std::string fmtbuf;
raw_string_ostream fmt(fmtbuf);
const char *radix_fmt = getRadixFmt();
// If OutputFormat is darwin and we have a MachOObjectFile print as darwin's
// size(1) -m output, else if OutputFormat is darwin and not a Mach-O object
// let it fall through to OutputFormat berkeley.
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Obj);
if (OutputFormat == darwin && MachO)
printDarwinSectionSizes(MachO);
// If we have a MachOObjectFile and the OutputFormat is berkeley print as
// darwin's default berkeley format for Mach-O files.
else if (MachO && OutputFormat == berkeley)
printDarwinSegmentSizes(MachO);
else if (OutputFormat == sysv) {
// Run two passes over all sections. The first gets the lengths needed for
// formatting the output. The second actually does the output.
std::size_t max_name_len = strlen("section");
std::size_t max_size_len = strlen("size");
std::size_t max_addr_len = strlen("addr");
for (const SectionRef &Section : Obj->sections()) {
if (!considerForSize(Obj, Section))
continue;
uint64_t size = Section.getSize();
total += size;
Expected<StringRef> name_or_err = Section.getName();
if (!name_or_err) {
error(name_or_err.takeError(), Obj->getFileName());
return;
}
uint64_t addr = Section.getAddress();
max_name_len = std::max(max_name_len, name_or_err->size());
max_size_len = std::max(max_size_len, getNumLengthAsString(size));
max_addr_len = std::max(max_addr_len, getNumLengthAsString(addr));
}
// Add extra padding.
max_name_len += 2;
max_size_len += 2;
max_addr_len += 2;
// Setup header format.
fmt << "%-" << max_name_len << "s "
<< "%" << max_size_len << "s "
<< "%" << max_addr_len << "s\n";
// Print header
outs() << format(fmt.str().c_str(), static_cast<const char *>("section"),
static_cast<const char *>("size"),
static_cast<const char *>("addr"));
fmtbuf.clear();
// Setup per section format.
fmt << "%-" << max_name_len << "s "
<< "%#" << max_size_len << radix_fmt << " "
<< "%#" << max_addr_len << radix_fmt << "\n";
// Print each section.
for (const SectionRef &Section : Obj->sections()) {
if (!considerForSize(Obj, Section))
continue;
Expected<StringRef> name_or_err = Section.getName();
if (!name_or_err) {
error(name_or_err.takeError(), Obj->getFileName());
return;
}
uint64_t size = Section.getSize();
uint64_t addr = Section.getAddress();
outs() << format(fmt.str().c_str(), name_or_err->str().c_str(), size, addr);
}
if (ELFCommons) {
uint64_t CommonSize = getCommonSize(Obj);
total += CommonSize;
outs() << format(fmt.str().c_str(), std::string("*COM*").c_str(),
CommonSize, static_cast<uint64_t>(0));
}
// Print total.
fmtbuf.clear();
fmt << "%-" << max_name_len << "s "
<< "%#" << max_size_len << radix_fmt << "\n";
outs() << format(fmt.str().c_str(), static_cast<const char *>("Total"),
total)
<< "\n\n";
} else {
// The Berkeley format does not display individual section sizes. It
// displays the cumulative size for each section type.
uint64_t total_text = 0;
uint64_t total_data = 0;
uint64_t total_bss = 0;
// Make one pass over the section table to calculate sizes.
for (const SectionRef &Section : Obj->sections()) {
uint64_t size = Section.getSize();
bool isText = Section.isBerkeleyText();
bool isData = Section.isBerkeleyData();
bool isBSS = Section.isBSS();
if (isText)
total_text += size;
else if (isData)
total_data += size;
else if (isBSS)
total_bss += size;
}
if (ELFCommons)
total_bss += getCommonSize(Obj);
total = total_text + total_data + total_bss;
if (TotalSizes) {
TotalObjectText += total_text;
TotalObjectData += total_data;
TotalObjectBss += total_bss;
TotalObjectTotal += total;
}
if (!BerkeleyHeaderPrinted) {
outs() << " text\t"
" data\t"
" bss\t"
" "
<< (Radix == octal ? "oct" : "dec")
<< "\t"
" hex\t"
"filename\n";
BerkeleyHeaderPrinted = true;
}
// Print result.
fmt << "%#7" << radix_fmt << "\t"
<< "%#7" << radix_fmt << "\t"
<< "%#7" << radix_fmt << "\t";
outs() << format(fmt.str().c_str(), total_text, total_data, total_bss);
fmtbuf.clear();
fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t"
<< "%7" PRIx64 "\t";
outs() << format(fmt.str().c_str(), total, total);
}
}
/// Checks to see if the @p O ObjectFile is a Mach-O file and if it is and there
/// is a list of architecture flags specified then check to make sure this
/// Mach-O file is one of those architectures or all architectures was
/// specificed. If not then an error is generated and this routine returns
/// false. Else it returns true.
static bool checkMachOAndArchFlags(ObjectFile *O, StringRef Filename) {
auto *MachO = dyn_cast<MachOObjectFile>(O);
if (!MachO || ArchAll || ArchFlags.empty())
return true;
MachO::mach_header H;
MachO::mach_header_64 H_64;
Triple T;
if (MachO->is64Bit()) {
H_64 = MachO->MachOObjectFile::getHeader64();
T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype);
} else {
H = MachO->MachOObjectFile::getHeader();
T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype);
}
if (none_of(ArchFlags, [&](const std::string &Name) {
return Name == T.getArchName();
})) {
error("no architecture specified", Filename);
return false;
}
return true;
}
/// Print the section sizes for @p file. If @p file is an archive, print the
/// section sizes for each archive member.
static void printFileSectionSizes(StringRef file) {
// Attempt to open the binary.
Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(file);
if (!BinaryOrErr) {
error(BinaryOrErr.takeError(), file);
return;
}
Binary &Bin = *BinaryOrErr.get().getBinary();
if (Archive *a = dyn_cast<Archive>(&Bin)) {
// This is an archive. Iterate over each member and display its sizes.
Error Err = Error::success();
for (auto &C : a->children(Err)) {
Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
if (!ChildOrErr) {
if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
error(std::move(E), a->getFileName(), C);
continue;
}
if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
if (!checkMachOAndArchFlags(o, file))
return;
if (OutputFormat == sysv)
outs() << o->getFileName() << " (ex " << a->getFileName() << "):\n";
else if (MachO && OutputFormat == darwin)
outs() << a->getFileName() << "(" << o->getFileName() << "):\n";
printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
if (MachO)
outs() << a->getFileName() << "(" << o->getFileName() << ")\n";
else
outs() << o->getFileName() << " (ex " << a->getFileName() << ")\n";
}
}
}
if (Err)
error(std::move(Err), a->getFileName());
} else if (MachOUniversalBinary *UB =
dyn_cast<MachOUniversalBinary>(&Bin)) {
// If we have a list of architecture flags specified dump only those.
if (!ArchAll && !ArchFlags.empty()) {
// Look for a slice in the universal binary that matches each ArchFlag.
bool ArchFound;
for (unsigned i = 0; i < ArchFlags.size(); ++i) {
ArchFound = false;
for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
E = UB->end_objects();
I != E; ++I) {
if (ArchFlags[i] == I->getArchFlagName()) {
ArchFound = true;
Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
if (UO) {
if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
if (OutputFormat == sysv)
outs() << o->getFileName() << " :\n";
else if (MachO && OutputFormat == darwin) {
if (MoreThanOneFile || ArchFlags.size() > 1)
outs() << o->getFileName() << " (for architecture "
<< I->getArchFlagName() << "): \n";
}
printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
if (!MachO || MoreThanOneFile || ArchFlags.size() > 1)
outs() << o->getFileName() << " (for architecture "
<< I->getArchFlagName() << ")";
outs() << "\n";
}
}
} else if (auto E = isNotObjectErrorInvalidFileType(
UO.takeError())) {
error(std::move(E), file, ArchFlags.size() > 1 ?
StringRef(I->getArchFlagName()) : StringRef());
return;
} else if (Expected<std::unique_ptr<Archive>> AOrErr =
I->getAsArchive()) {
std::unique_ptr<Archive> &UA = *AOrErr;
// This is an archive. Iterate over each member and display its
// sizes.
Error Err = Error::success();
for (auto &C : UA->children(Err)) {
Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
if (!ChildOrErr) {
if (auto E = isNotObjectErrorInvalidFileType(
ChildOrErr.takeError()))
error(std::move(E), UA->getFileName(), C,
ArchFlags.size() > 1 ?
StringRef(I->getArchFlagName()) : StringRef());
continue;
}
if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
if (OutputFormat == sysv)
outs() << o->getFileName() << " (ex " << UA->getFileName()
<< "):\n";
else if (MachO && OutputFormat == darwin)
outs() << UA->getFileName() << "(" << o->getFileName()
<< ")"
<< " (for architecture " << I->getArchFlagName()
<< "):\n";
printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
if (MachO) {
outs() << UA->getFileName() << "(" << o->getFileName()
<< ")";
if (ArchFlags.size() > 1)
outs() << " (for architecture " << I->getArchFlagName()
<< ")";
outs() << "\n";
} else
outs() << o->getFileName() << " (ex " << UA->getFileName()
<< ")\n";
}
}
}
if (Err)
error(std::move(Err), UA->getFileName());
} else {
consumeError(AOrErr.takeError());
error("mach-o universal file for architecture " +
StringRef(I->getArchFlagName()) +
" is not a mach-o file or an archive file",
file);
}
}
}
if (!ArchFound) {
error("file does not contain architecture " + ArchFlags[i], file);
return;
}
}
return;
}
// No architecture flags were specified so if this contains a slice that
// matches the host architecture dump only that.
if (!ArchAll) {
StringRef HostArchName = MachOObjectFile::getHostArch().getArchName();
for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
E = UB->end_objects();
I != E; ++I) {
if (HostArchName == I->getArchFlagName()) {
Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
if (UO) {
if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
if (OutputFormat == sysv)
outs() << o->getFileName() << " :\n";
else if (MachO && OutputFormat == darwin) {
if (MoreThanOneFile)
outs() << o->getFileName() << " (for architecture "
<< I->getArchFlagName() << "):\n";
}
printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
if (!MachO || MoreThanOneFile)
outs() << o->getFileName() << " (for architecture "
<< I->getArchFlagName() << ")";
outs() << "\n";
}
}
} else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) {
error(std::move(E), file);
return;
} else if (Expected<std::unique_ptr<Archive>> AOrErr =
I->getAsArchive()) {
std::unique_ptr<Archive> &UA = *AOrErr;
// This is an archive. Iterate over each member and display its
// sizes.
Error Err = Error::success();
for (auto &C : UA->children(Err)) {
Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
if (!ChildOrErr) {
if (auto E = isNotObjectErrorInvalidFileType(
ChildOrErr.takeError()))
error(std::move(E), UA->getFileName(), C);
continue;
}
if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
if (OutputFormat == sysv)
outs() << o->getFileName() << " (ex " << UA->getFileName()
<< "):\n";
else if (MachO && OutputFormat == darwin)
outs() << UA->getFileName() << "(" << o->getFileName() << ")"
<< " (for architecture " << I->getArchFlagName()
<< "):\n";
printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
if (MachO)
outs() << UA->getFileName() << "(" << o->getFileName()
<< ")\n";
else
outs() << o->getFileName() << " (ex " << UA->getFileName()
<< ")\n";
}
}
}
if (Err)
error(std::move(Err), UA->getFileName());
} else {
consumeError(AOrErr.takeError());
error("mach-o universal file for architecture " +
StringRef(I->getArchFlagName()) +
" is not a mach-o file or an archive file",
file);
}
return;
}
}
}
// Either all architectures have been specified or none have been specified
// and this does not contain the host architecture so dump all the slices.
bool MoreThanOneArch = UB->getNumberOfObjects() > 1;
for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
E = UB->end_objects();
I != E; ++I) {
Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
if (UO) {
if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
if (OutputFormat == sysv)
outs() << o->getFileName() << " :\n";
else if (MachO && OutputFormat == darwin) {
if (MoreThanOneFile || MoreThanOneArch)
outs() << o->getFileName() << " (for architecture "
<< I->getArchFlagName() << "):";
outs() << "\n";
}
printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
if (!MachO || MoreThanOneFile || MoreThanOneArch)
outs() << o->getFileName() << " (for architecture "
<< I->getArchFlagName() << ")";
outs() << "\n";
}
}
} else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) {
error(std::move(E), file, MoreThanOneArch ?
StringRef(I->getArchFlagName()) : StringRef());
return;
} else if (Expected<std::unique_ptr<Archive>> AOrErr =
I->getAsArchive()) {
std::unique_ptr<Archive> &UA = *AOrErr;
// This is an archive. Iterate over each member and display its sizes.
Error Err = Error::success();
for (auto &C : UA->children(Err)) {
Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
if (!ChildOrErr) {
if (auto E = isNotObjectErrorInvalidFileType(
ChildOrErr.takeError()))
error(std::move(E), UA->getFileName(), C, MoreThanOneArch ?
StringRef(I->getArchFlagName()) : StringRef());
continue;
}
if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
if (OutputFormat == sysv)
outs() << o->getFileName() << " (ex " << UA->getFileName()
<< "):\n";
else if (MachO && OutputFormat == darwin)
outs() << UA->getFileName() << "(" << o->getFileName() << ")"
<< " (for architecture " << I->getArchFlagName() << "):\n";
printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
if (MachO)
outs() << UA->getFileName() << "(" << o->getFileName() << ")"
<< " (for architecture " << I->getArchFlagName()
<< ")\n";
else
outs() << o->getFileName() << " (ex " << UA->getFileName()
<< ")\n";
}
}
}
if (Err)
error(std::move(Err), UA->getFileName());
} else {
consumeError(AOrErr.takeError());
error("mach-o universal file for architecture " +
StringRef(I->getArchFlagName()) +
" is not a mach-o file or an archive file",
file);
}
}
} else if (ObjectFile *o = dyn_cast<ObjectFile>(&Bin)) {
if (!checkMachOAndArchFlags(o, file))
return;
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
if (OutputFormat == sysv)
outs() << o->getFileName() << " :\n";
else if (MachO && OutputFormat == darwin && MoreThanOneFile)
outs() << o->getFileName() << ":\n";
printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
if (!MachO || MoreThanOneFile)
outs() << o->getFileName();
outs() << "\n";
}
} else {
error("unsupported file type", file);
}
}
static void printBerkeleyTotals() {
std::string fmtbuf;
raw_string_ostream fmt(fmtbuf);
const char *radix_fmt = getRadixFmt();
fmt << "%#7" << radix_fmt << "\t"
<< "%#7" << radix_fmt << "\t"
<< "%#7" << radix_fmt << "\t";
outs() << format(fmt.str().c_str(), TotalObjectText, TotalObjectData,
TotalObjectBss);
fmtbuf.clear();
fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t"
<< "%7" PRIx64 "\t";
outs() << format(fmt.str().c_str(), TotalObjectTotal, TotalObjectTotal)
<< "(TOTALS)\n";
}
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
cl::HideUnrelatedOptions(SizeCat);
cl::ParseCommandLineOptions(argc, argv, "llvm object size dumper\n");
ToolName = argv[0];
if (OutputFormatShort.getNumOccurrences())
OutputFormat = static_cast<OutputFormatTy>(OutputFormatShort);
if (RadixShort.getNumOccurrences())
Radix = RadixShort.getValue();
for (StringRef Arch : ArchFlags) {
if (Arch == "all") {
ArchAll = true;
} else {
if (!MachOObjectFile::isValidArch(Arch)) {
outs() << ToolName << ": for the -arch option: Unknown architecture "
<< "named '" << Arch << "'";
return 1;
}
}
}
if (InputFilenames.empty())
InputFilenames.push_back("a.out");
MoreThanOneFile = InputFilenames.size() > 1;
llvm::for_each(InputFilenames, printFileSectionSizes);
if (OutputFormat == berkeley && TotalSizes)
printBerkeleyTotals();
if (HadError)
return 1;
}

View File

@ -0,0 +1,120 @@
//===-- llvm-strings.cpp - Printable String dumping utility ---------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This program is a utility that works like binutils "strings", that is, it
// prints out printable strings in a binary, objdump, or archive file.
//
//===----------------------------------------------------------------------===//
#include "llvm/Object/Binary.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Program.h"
#include <cctype>
#include <string>
using namespace llvm;
using namespace llvm::object;
static cl::list<std::string> InputFileNames(cl::Positional,
cl::desc("<input object files>"),
cl::ZeroOrMore);
static cl::opt<bool>
PrintFileName("print-file-name",
cl::desc("Print the name of the file before each string"));
static cl::alias PrintFileNameShort("f", cl::desc(""),
cl::aliasopt(PrintFileName));
static cl::opt<int>
MinLength("bytes", cl::desc("Print sequences of the specified length"),
cl::init(4));
static cl::alias MinLengthShort("n", cl::desc(""), cl::aliasopt(MinLength));
static cl::opt<bool>
AllSections("all",
cl::desc("Check all sections, not just the data section"));
static cl::alias AllSectionsShort("a", cl::desc(""),
cl::aliasopt(AllSections));
enum radix { none, octal, hexadecimal, decimal };
static cl::opt<radix>
Radix("radix", cl::desc("print the offset within the file"),
cl::values(clEnumValN(octal, "o", "octal"),
clEnumValN(hexadecimal, "x", "hexadecimal"),
clEnumValN(decimal, "d", "decimal")),
cl::init(none));
static cl::alias RadixShort("t", cl::desc(""), cl::aliasopt(Radix));
static cl::extrahelp
HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) {
auto print = [&OS, FileName](unsigned Offset, StringRef L) {
if (L.size() < static_cast<size_t>(MinLength))
return;
if (PrintFileName)
OS << FileName << ": ";
switch (Radix) {
case none:
break;
case octal:
OS << format("%7o ", Offset);
break;
case hexadecimal:
OS << format("%7x ", Offset);
break;
case decimal:
OS << format("%7u ", Offset);
break;
}
OS << L << '\n';
};
const char *B = Contents.begin();
const char *P = nullptr, *E = nullptr, *S = nullptr;
for (P = Contents.begin(), E = Contents.end(); P < E; ++P) {
if (isPrint(*P) || *P == '\t') {
if (S == nullptr)
S = P;
} else if (S) {
print(S - B, StringRef(S, P - S));
S = nullptr;
}
}
if (S)
print(S - B, StringRef(S, E - S));
}
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
cl::ParseCommandLineOptions(argc, argv, "llvm string dumper\n");
if (MinLength == 0) {
errs() << "invalid minimum string length 0\n";
return EXIT_FAILURE;
}
if (InputFileNames.empty())
InputFileNames.push_back("-");
for (const auto &File : InputFileNames) {
ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
MemoryBuffer::getFileOrSTDIN(File);
if (std::error_code EC = Buffer.getError())
errs() << File << ": " << EC.message() << '\n';
else
strings(llvm::outs(), File == "-" ? "{standard input}" : File,
Buffer.get()->getMemBufferRef().getBuffer());
}
return EXIT_SUCCESS;
}

View File

@ -1,14 +1,14 @@
// $FreeBSD$
#define LLVM_REVISION "llvmorg-10.0.0-129-gd24d5c8e308"
#define LLVM_REVISION "llvmorg-10.0.1-rc2-0-g77d76b71d7d"
#define LLVM_REPOSITORY "git@github.com:llvm/llvm-project.git"
#define CLANG_REVISION "llvmorg-10.0.0-129-gd24d5c8e308"
#define CLANG_REVISION "llvmorg-10.0.1-rc2-0-g77d76b71d7d"
#define CLANG_REPOSITORY "git@github.com:llvm/llvm-project.git"
// <Upstream revision at import>-<Local identifier in __FreeBSD_version style>
#define LLD_REVISION "llvmorg-10.0.0-129-gd24d5c8e308-1300007"
#define LLD_REVISION "llvmorg-10.0.1-rc2-0-g77d76b71d7d-1300007"
#define LLD_REPOSITORY "FreeBSD"
#define LLDB_REVISION "llvmorg-10.0.0-129-gd24d5c8e308"
#define LLDB_REVISION "llvmorg-10.0.1-rc2-0-g77d76b71d7d"
#define LLDB_REPOSITORY "git@github.com:llvm/llvm-project.git"

View File

@ -1,3 +1,3 @@
/* $FreeBSD$ */
#define LLVM_REVISION "llvmorg-10.0.0-129-gd24d5c8e308"
#define LLVM_REVISION "llvmorg-10.0.1-rc2-0-g77d76b71d7d"
#define LLVM_REPOSITORY "git@github.com:llvm/llvm-project.git"

View File

@ -1493,6 +1493,7 @@ OLD_FILES+=usr/bin/llvm-cxxfilt
OLD_FILES+=usr/bin/llvm-diff
OLD_FILES+=usr/bin/llvm-dis
OLD_FILES+=usr/bin/llvm-dwarfdump
OLD_FILES+=usr/bin/llvm-dwp
OLD_FILES+=usr/bin/llvm-extract
OLD_FILES+=usr/bin/llvm-link
OLD_FILES+=usr/bin/llvm-lto
@ -1503,6 +1504,8 @@ OLD_FILES+=usr/bin/llvm-modextract
OLD_FILES+=usr/bin/llvm-objcopy
OLD_FILES+=usr/bin/llvm-pdbutil
OLD_FILES+=usr/bin/llvm-rtdyld
OLD_FILES+=usr/bin/llvm-size
OLD_FILES+=usr/bin/llvm-strings
OLD_FILES+=usr/bin/llvm-xray
OLD_FILES+=usr/bin/opt
OLD_FILES+=usr/share/man/man1/bugpoint.1.gz
@ -1518,6 +1521,8 @@ OLD_FILES+=usr/share/man/man1/llvm-extract.1.gz
OLD_FILES+=usr/share/man/man1/llvm-link.1.gz
OLD_FILES+=usr/share/man/man1/llvm-objcopy.1.gz
OLD_FILES+=usr/share/man/man1/llvm-pdbutil.1.gz
OLD_FILES+=usr/share/man/man1/llvm-size.1.gz
OLD_FILES+=usr/share/man/man1/llvm-strings.1.gz
OLD_FILES+=usr/share/man/man1/opt.1.gz
.endif
@ -2078,6 +2083,7 @@ OLD_FILES+=usr/share/man/man1/gcov.1.gz
OLD_FILES+=usr/bin/llvm-cov
OLD_FILES+=usr/bin/llvm-profdata
OLD_FILES+=usr/share/man/man1/llvm-cov.1.gz
OLD_FILES+=usr/share/man/man1/llvm-profdata.1.gz
.endif
.if ${MK_GDB} == no

View File

@ -27,6 +27,7 @@ SUBDIR+= llvm-cxxfilt
SUBDIR+= llvm-diff
SUBDIR+= llvm-dis
SUBDIR+= llvm-dwarfdump
SUBDIR+= llvm-dwp
SUBDIR+= llvm-extract
SUBDIR+= llvm-link
SUBDIR+= llvm-lto
@ -37,6 +38,8 @@ SUBDIR+= llvm-modextract
SUBDIR+= llvm-objcopy
SUBDIR+= llvm-pdbutil
SUBDIR+= llvm-rtdyld
SUBDIR+= llvm-size
SUBDIR+= llvm-strings
SUBDIR+= llvm-xray
SUBDIR+= opt
.endif

View File

@ -0,0 +1,12 @@
# $FreeBSD$
PROG_CXX= llvm-dwp
MAN=
SRCDIR= llvm/tools/llvm-dwp
SRCS+= DWPError.cpp
SRCS+= llvm-dwp.cpp
LIBADD+= z
.include "../llvm.prog.mk"

View File

@ -0,0 +1,8 @@
# $FreeBSD$
PROG_CXX= llvm-size
SRCDIR= llvm/tools/llvm-size
SRCS+= llvm-size.cpp
.include "../llvm.prog.mk"

View File

@ -0,0 +1,275 @@
.\" $FreeBSD$
.\" Man page generated from reStructuredText.
.
.TH "LLVM-SIZE" "1" "2020-06-26" "10" "LLVM"
.SH NAME
llvm-size \- print size information
.
.nr rst2man-indent-level 0
.
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.SH SYNOPSIS
.sp
\fBllvm\-size\fP [\fIoptions\fP] [\fIinput...\fP]
.SH DESCRIPTION
.sp
\fBllvm\-size\fP is a tool that prints size information for binary files.
It is intended to be a drop\-in replacement for GNU\(aqs \fBsize\fP\&.
.sp
The tool prints size information for each \fBinput\fP specified. If no input is
specified, the program prints size information for \fBa.out\fP\&. If "\fB\-\fP" is
specified as an input file, \fBllvm\-size\fP reads a file from the standard
input stream. If an input is an archive, size information will be displayed for
all its members.
.SH OPTIONS
.INDENT 0.0
.TP
.B \-A
Equivalent to \fI\%\-\-format\fP with a value of \fBsysv\fP\&.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-arch=<arch>
Architecture(s) from Mach\-O universal binaries to display information for.
.UNINDENT
.INDENT 0.0
.TP
.B \-B
Equivalent to \fI\%\-\-format\fP with a value of \fBberkeley\fP\&.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-common
Include ELF common symbol sizes in bss size for \fBberkeley\fP output format, or
as a separate section entry for \fBsysv\fP output. If not specified, these
symbols are ignored.
.UNINDENT
.INDENT 0.0
.TP
.B \-d
Equivalent to \fI\%\-\-radix\fP with a value of \fB10\fP\&.
.UNINDENT
.INDENT 0.0
.TP
.B \-l
Display verbose address and offset information for segments and sections in
Mach\-O files in \fBdarwin\fP format.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-format=<format>
Set the output format to the \fB<format>\fP specified. Available \fB<format>\fP
options are \fBberkeley\fP (the default), \fBsysv\fP and \fBdarwin\fP\&.
.sp
Berkeley output summarises text, data and bss sizes in each file, as shown
below for a typical pair of ELF files:
.INDENT 7.0
.INDENT 3.5
.sp
.nf
.ft C
$ llvm\-size \-\-format=berkeley test.o test2.o
text data bss dec hex filename
182 16 5 203 cb test.elf
82 8 1 91 5b test2.o
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
For Mach\-O files, the output format is slightly different:
.INDENT 7.0
.INDENT 3.5
.sp
.nf
.ft C
$ llvm\-size \-\-format=berkeley macho.obj macho2.obj
__TEXT __DATA __OBJC others dec hex
4 8 0 0 12 c macho.obj
16 32 0 0 48 30 macho2.obj
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
Sysv output displays size and address information for most sections, with each
file being listed separately:
.INDENT 7.0
.INDENT 3.5
.sp
.nf
.ft C
$ llvm\-size \-\-format=sysv test.elf test2.o
test.elf :
section size addr
.eh_frame 92 2097496
.text 90 2101248
.data 16 2105344
.bss 5 2105360
.comment 209 0
Total 412
test2.o :
section size addr
.text 26 0
.data 8 0
.bss 1 0
.comment 106 0
.note.GNU\-stack 0 0
.eh_frame 56 0
.llvm_addrsig 2 0
Total 199
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
\fBdarwin\fP format only affects Mach\-O input files. If an input of a different
file format is specified, \fBllvm\-size\fP falls back to \fBberkeley\fP
format. When producing \fBdarwin\fP format, the tool displays information about
segments and sections:
.INDENT 7.0
.INDENT 3.5
.sp
.nf
.ft C
$ llvm\-size \-\-format=darwin macho.obj macho2.obj
macho.obj:
Segment : 12
Section (__TEXT, __text): 4
Section (__DATA, __data): 8
total 12
total 12
macho2.obj:
Segment : 48
Section (__TEXT, __text): 16
Section (__DATA, __data): 32
total 48
total 48
.ft P
.fi
.UNINDENT
.UNINDENT
.UNINDENT
.INDENT 0.0
.TP
.B \-\-help, \-h
Display a summary of command line options.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-help\-list
Display an uncategorized summary of command line options.
.UNINDENT
.INDENT 0.0
.TP
.B \-m
Equivalent to \fI\%\-\-format\fP with a value of \fBdarwin\fP\&.
.UNINDENT
.INDENT 0.0
.TP
.B \-o
Equivalent to \fI\%\-\-radix\fP with a value of \fB8\fP\&.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-radix=<value>
Display size information in the specified radix. Permitted values are \fB8\fP,
\fB10\fP (the default) and \fB16\fP for octal, decimal and hexadecimal output
respectively.
.sp
Example:
.INDENT 7.0
.INDENT 3.5
.sp
.nf
.ft C
$ llvm\-size \-\-radix=8 test.o
text data bss oct hex filename
0152 04 04 162 72 test.o
$ llvm\-size \-\-radix=10 test.o
text data bss dec hex filename
106 4 4 114 72 test.o
$ llvm\-size \-\-radix=16 test.o
text data bss dec hex filename
0x6a 0x4 0x4 114 72 test.o
.ft P
.fi
.UNINDENT
.UNINDENT
.UNINDENT
.INDENT 0.0
.TP
.B \-\-totals, \-t
Applies only to \fBberkeley\fP output format. Display the totals for all listed
fields, in addition to the individual file listings.
.sp
Example:
.INDENT 7.0
.INDENT 3.5
.sp
.nf
.ft C
$ llvm\-size \-\-totals test.elf test2.o
text data bss dec hex filename
182 16 5 203 cb test.elf
82 8 1 91 5b test2.o
264 24 6 294 126 (TOTALS)
.ft P
.fi
.UNINDENT
.UNINDENT
.UNINDENT
.INDENT 0.0
.TP
.B \-\-version
Display the version of the \fBllvm\-size\fP executable.
.UNINDENT
.INDENT 0.0
.TP
.B \-x
Equivalent to \fI\%\-\-radix\fP with a value of \fB16\fP\&.
.UNINDENT
.INDENT 0.0
.TP
.B @<FILE>
Read command\-line options from response file \fB<FILE>\fP\&.
.UNINDENT
.SH EXIT STATUS
.sp
\fBllvm\-size\fP exits with a non\-zero exit code if there is an error.
Otherwise, it exits with code 0.
.SH BUGS
.sp
To report bugs, please visit <\fI\%http://llvm.org/bugs/\fP>.
.SH AUTHOR
Maintained by the LLVM Team (https://llvm.org/).
.SH COPYRIGHT
2003-2020, LLVM Project
.\" Generated by docutils manpage writer.
.

View File

@ -0,0 +1,9 @@
# $FreeBSD$
PROG_CXX= llvm-strings
MAN=
SRCDIR= llvm/tools/llvm-strings
SRCS+= llvm-strings.cpp
.include "../llvm.prog.mk"

View File

@ -0,0 +1,179 @@
.\" $FreeBSD$
.\" Man page generated from reStructuredText.
.
.TH "LLVM-STRINGS" "1" "2020-06-26" "10" "LLVM"
.SH NAME
llvm-strings \- print strings
.
.nr rst2man-indent-level 0
.
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.SH SYNOPSIS
.sp
\fBllvm\-strings\fP [\fIoptions\fP] [\fIinput...\fP]
.SH DESCRIPTION
.sp
\fBllvm\-strings\fP is a tool intended as a drop\-in replacement for GNU\(aqs
\fBstrings\fP, which looks for printable strings in files and writes them
to the standard output stream. A printable string is any sequence of four (by
default) or more printable ASCII characters. The end of the file, or any other
byte, terminates the current sequence.
.sp
\fBllvm\-strings\fP looks for strings in each \fBinput\fP file specified.
Unlike GNU \fBstrings\fP it looks in the entire input file, regardless of
file format, rather than restricting the search to certain sections of object
files. If "\fB\-\fP" is specified as an \fBinput\fP, or no \fBinput\fP is specified,
the program reads from the standard input stream.
.SH EXAMPLE
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
$ cat input.txt
bars
foo
wibble blob
$ llvm\-strings input.txt
bars
wibble blob
.ft P
.fi
.UNINDENT
.UNINDENT
.SH OPTIONS
.INDENT 0.0
.TP
.B \-\-all, \-a
Silently ignored. Present for GNU \fBstrings\fP compatibility.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-bytes=<length>, \-n
Set the minimum number of printable ASCII characters required for a sequence of
bytes to be considered a string. The default value is 4.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-help, \-h
Display a summary of command line options.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-help\-list
Display an uncategorized summary of command line options.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-print\-file\-name, \-f
Display the name of the containing file before each string.
.sp
Example:
.INDENT 7.0
.INDENT 3.5
.sp
.nf
.ft C
$ llvm\-strings \-\-print\-file\-name test.o test.elf
test.o: _Z5hellov
test.o: some_bss
test.o: test.cpp
test.o: main
test.elf: test.cpp
test.elf: test2.cpp
test.elf: _Z5hellov
test.elf: main
test.elf: some_bss
.ft P
.fi
.UNINDENT
.UNINDENT
.UNINDENT
.INDENT 0.0
.TP
.B \-\-radix=<radix>, \-t
Display the offset within the file of each string, before the string and using
the specified radix. Valid \fB<radix>\fP values are \fBo\fP, \fBd\fP and \fBx\fP for
octal, decimal and hexadecimal respectively.
.sp
Example:
.INDENT 7.0
.INDENT 3.5
.sp
.nf
.ft C
$ llvm\-strings \-\-radix=o test.o
1054 _Z5hellov
1066 .rela.text
1101 .comment
1112 some_bss
1123 .bss
1130 test.cpp
1141 main
$ llvm\-strings \-\-radix=d test.o
556 _Z5hellov
566 .rela.text
577 .comment
586 some_bss
595 .bss
600 test.cpp
609 main
$ llvm\-strings \-t x test.o
22c _Z5hellov
236 .rela.text
241 .comment
24a some_bss
253 .bss
258 test.cpp
261 main
.ft P
.fi
.UNINDENT
.UNINDENT
.UNINDENT
.INDENT 0.0
.TP
.B \-\-version
Display the version of the \fBllvm\-strings\fP executable.
.UNINDENT
.INDENT 0.0
.TP
.B @<FILE>
Read command\-line options from response file \fB<FILE>\fP\&.
.UNINDENT
.SH EXIT STATUS
.sp
\fBllvm\-strings\fP exits with a non\-zero exit code if there is an error.
Otherwise, it exits with code 0.
.SH BUGS
.sp
To report bugs, please visit <\fI\%http://llvm.org/bugs/\fP>.
.SH AUTHOR
Maintained by the LLVM Team (https://llvm.org/).
.SH COPYRIGHT
2003-2020, LLVM Project
.\" Generated by docutils manpage writer.
.