Vendor import of llvm-project branch release/10.x
llvmorg-10.0.1-rc2-0-g77d76b71d7d.
This commit is contained in:
parent
b00e6b27b0
commit
8046d1ca69
@ -9613,7 +9613,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();
|
||||
}
|
||||
|
31
compiler-rt/lib/builtins/riscv/int_mul_impl.inc
Normal file
31
compiler-rt/lib/builtins/riscv/int_mul_impl.inc
Normal 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
|
11
compiler-rt/lib/builtins/riscv/muldi3.S
Normal file
11
compiler-rt/lib/builtins/riscv/muldi3.S
Normal 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
|
@ -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
|
||||
|
@ -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)>;
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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");
|
||||
|
3
llvm/tools/llvm-dwp/DWPError.cpp
Normal file
3
llvm/tools/llvm-dwp/DWPError.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
#include "DWPError.h"
|
||||
using namespace llvm;
|
||||
char DWPError::ID;
|
23
llvm/tools/llvm-dwp/DWPError.h
Normal file
23
llvm/tools/llvm-dwp/DWPError.h
Normal 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
|
56
llvm/tools/llvm-dwp/DWPStringPool.h
Normal file
56
llvm/tools/llvm-dwp/DWPStringPool.h
Normal 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
|
749
llvm/tools/llvm-dwp/llvm-dwp.cpp
Normal file
749
llvm/tools/llvm-dwp/llvm-dwp.cpp
Normal 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;
|
||||
}
|
894
llvm/tools/llvm-size/llvm-size.cpp
Normal file
894
llvm/tools/llvm-size/llvm-size.cpp
Normal 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;
|
||||
}
|
120
llvm/tools/llvm-strings/llvm-strings.cpp
Normal file
120
llvm/tools/llvm-strings/llvm-strings.cpp
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user