Merge "ART: Make the dominator bitvectors expandable"
diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h
index 6272555..b95789e 100644
--- a/compiler/dex/quick/arm/arm_lir.h
+++ b/compiler/dex/quick/arm/arm_lir.h
@@ -556,22 +556,24 @@
// Instruction assembly field_loc kind.
enum ArmEncodingKind {
- kFmtUnused, // Unused field and marks end of formats.
- kFmtBitBlt, // Bit string using end/start.
- kFmtDfp, // Double FP reg.
- kFmtSfp, // Single FP reg.
- kFmtModImm, // Shifted 8-bit immed using [26,14..12,7..0].
- kFmtImm16, // Zero-extended immed using [26,19..16,14..12,7..0].
- kFmtImm6, // Encoded branch target using [9,7..3]0.
- kFmtImm12, // Zero-extended immediate using [26,14..12,7..0].
- kFmtShift, // Shift descriptor, [14..12,7..4].
- kFmtLsb, // least significant bit using [14..12][7..6].
- kFmtBWidth, // bit-field width, encoded as width-1.
- kFmtShift5, // Shift count, [14..12,7..6].
- kFmtBrOffset, // Signed extended [26,11,13,21-16,10-0]:0.
- kFmtFPImm, // Encoded floating point immediate.
- kFmtOff24, // 24-bit Thumb2 unconditional branch encoding.
- kFmtSkip, // Unused field, but continue to next.
+ kFmtUnused, // Unused field and marks end of formats.
+ kFmtBitBlt, // Bit string using end/start.
+ kFmtLdmRegList, // Load multiple register list using [15,14,12..0].
+ kFmtStmRegList, // Store multiple register list using [14,12..0].
+ kFmtDfp, // Double FP reg.
+ kFmtSfp, // Single FP reg.
+ kFmtModImm, // Shifted 8-bit immed using [26,14..12,7..0].
+ kFmtImm16, // Zero-extended immed using [26,19..16,14..12,7..0].
+ kFmtImm6, // Encoded branch target using [9,7..3]0.
+ kFmtImm12, // Zero-extended immediate using [26,14..12,7..0].
+ kFmtShift, // Shift descriptor, [14..12,7..4].
+ kFmtLsb, // least significant bit using [14..12][7..6].
+ kFmtBWidth, // bit-field width, encoded as width-1.
+ kFmtShift5, // Shift count, [14..12,7..6].
+ kFmtBrOffset, // Signed extended [26,11,13,21-16,10-0]:0.
+ kFmtFPImm, // Encoded floating point immediate.
+ kFmtOff24, // 24-bit Thumb2 unconditional branch encoding.
+ kFmtSkip, // Unused field, but continue to next.
};
// Struct used to define the snippet positions for each Thumb opcode.
diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc
index 35c3597..06d9dd5 100644
--- a/compiler/dex/quick/arm/assemble_arm.cc
+++ b/compiler/dex/quick/arm/assemble_arm.cc
@@ -560,12 +560,12 @@
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1 | IS_MOVE,
"vmov.f64 ", " !0S, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2Ldmia, 0xe8900000,
- kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtBitBlt, 19, 16, kFmtLdmRegList, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
"ldmia", "!0C!!, <!1R>", 4, kFixupNone),
ENCODING_MAP(kThumb2Stmia, 0xe8800000,
- kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtBitBlt, 19, 16, kFmtStmRegList, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_USE_LIST1 | IS_STORE,
"stmia", "!0C!!, <!1R>", 4, kFixupNone),
@@ -935,7 +935,7 @@
IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD_OFF,
"ldr", "!0C, [r15pc, -#!1d]", 4, kFixupNone),
ENCODING_MAP(kThumb2Stm, 0xe9000000,
- kFmtBitBlt, 19, 16, kFmtBitBlt, 12, 0, kFmtUnused, -1, -1,
+ kFmtBitBlt, 19, 16, kFmtStmRegList, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_USE0 | REG_USE_LIST1 | IS_STORE,
"stm", "!0C, <!1R>", 4, kFixupNone),
@@ -992,7 +992,7 @@
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0 | NEEDS_FIXUP,
"movt", "!0C, #!1M", 4, kFixupMovImmHST),
ENCODING_MAP(kThumb2LdmiaWB, 0xe8b00000,
- kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtBitBlt, 19, 16, kFmtLdmRegList, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
"ldmia", "!0C!!, <!1R>", 4, kFixupNone),
@@ -1094,6 +1094,19 @@
bits |= value;
} else {
switch (encoder->field_loc[i].kind) {
+ case kFmtLdmRegList:
+ value = (operand << encoder->field_loc[i].start) &
+ ((1 << (encoder->field_loc[i].end + 1)) - 1);
+ bits |= value;
+ DCHECK_EQ((bits & (1 << 13)), 0u);
+ break;
+ case kFmtStmRegList:
+ value = (operand << encoder->field_loc[i].start) &
+ ((1 << (encoder->field_loc[i].end + 1)) - 1);
+ bits |= value;
+ DCHECK_EQ((bits & (1 << 13)), 0u);
+ DCHECK_EQ((bits & (1 << 15)), 0u);
+ break;
case kFmtSkip:
break; // Nothing to do, but continue to next.
case kFmtUnused:
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index a85e02f..dd4d661 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -22,6 +22,7 @@
#include "x86_lir.h"
#include <map>
+#include <vector>
namespace art {
@@ -61,6 +62,15 @@
bool initialized_;
};
+ class ExplicitTempRegisterLock {
+ public:
+ ExplicitTempRegisterLock(X86Mir2Lir* mir_to_lir, int n_regs, ...);
+ ~ExplicitTempRegisterLock();
+ protected:
+ std::vector<RegStorage> temp_regs_;
+ X86Mir2Lir* const mir_to_lir_;
+ };
+
public:
X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 196155e..3ca85bf 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -49,8 +49,8 @@
return;
}
- FlushAllRegs();
- LockCallTemps(); // Prepare for explicit register usage
+ // Prepare for explicit register usage
+ ExplicitTempRegisterLock(this, 4, &rs_r0, &rs_r1, &rs_r2, &rs_r3);
RegStorage r_tmp1 = RegStorage::MakeRegPair(rs_r0, rs_r1);
RegStorage r_tmp2 = RegStorage::MakeRegPair(rs_r2, rs_r3);
LoadValueDirectWideFixed(rl_src1, r_tmp1);
@@ -274,7 +274,6 @@
// Avoid using float regs here.
RegisterClass src_reg_class = rl_src.ref ? kRefReg : kCoreReg;
RegisterClass result_reg_class = rl_dest.ref ? kRefReg : kCoreReg;
- rl_src = LoadValue(rl_src, src_reg_class);
ConditionCode ccode = mir->meta.ccode;
// The kMirOpSelect has two variants, one for constants and one for moves.
@@ -283,58 +282,68 @@
if (is_constant_case) {
int true_val = mir->dalvikInsn.vB;
int false_val = mir->dalvikInsn.vC;
- rl_result = EvalLoc(rl_dest, result_reg_class, true);
- /*
- * For ccode == kCondEq:
- *
- * 1) When the true case is zero and result_reg is not same as src_reg:
- * xor result_reg, result_reg
- * cmp $0, src_reg
- * mov t1, $false_case
- * cmovnz result_reg, t1
- * 2) When the false case is zero and result_reg is not same as src_reg:
- * xor result_reg, result_reg
- * cmp $0, src_reg
- * mov t1, $true_case
- * cmovz result_reg, t1
- * 3) All other cases (we do compare first to set eflags):
- * cmp $0, src_reg
- * mov result_reg, $false_case
- * mov t1, $true_case
- * cmovz result_reg, t1
- */
- // FIXME: depending on how you use registers you could get a false != mismatch when dealing
- // with different views of the same underlying physical resource (i.e. solo32 vs. solo64).
- const bool result_reg_same_as_src =
- (rl_src.location == kLocPhysReg && rl_src.reg.GetRegNum() == rl_result.reg.GetRegNum());
- const bool true_zero_case = (true_val == 0 && false_val != 0 && !result_reg_same_as_src);
- const bool false_zero_case = (false_val == 0 && true_val != 0 && !result_reg_same_as_src);
- const bool catch_all_case = !(true_zero_case || false_zero_case);
+ // simplest strange case
+ if (true_val == false_val) {
+ rl_result = EvalLoc(rl_dest, result_reg_class, true);
+ LoadConstantNoClobber(rl_result.reg, true_val);
+ } else {
+ // TODO: use GenSelectConst32 and handle additional opcode patterns such as
+ // "cmp; setcc; movzx" or "cmp; sbb r0,r0; and r0,$mask; add r0,$literal".
+ rl_src = LoadValue(rl_src, src_reg_class);
+ rl_result = EvalLoc(rl_dest, result_reg_class, true);
+ /*
+ * For ccode == kCondEq:
+ *
+ * 1) When the true case is zero and result_reg is not same as src_reg:
+ * xor result_reg, result_reg
+ * cmp $0, src_reg
+ * mov t1, $false_case
+ * cmovnz result_reg, t1
+ * 2) When the false case is zero and result_reg is not same as src_reg:
+ * xor result_reg, result_reg
+ * cmp $0, src_reg
+ * mov t1, $true_case
+ * cmovz result_reg, t1
+ * 3) All other cases (we do compare first to set eflags):
+ * cmp $0, src_reg
+ * mov result_reg, $false_case
+ * mov t1, $true_case
+ * cmovz result_reg, t1
+ */
+ // FIXME: depending on how you use registers you could get a false != mismatch when dealing
+ // with different views of the same underlying physical resource (i.e. solo32 vs. solo64).
+ const bool result_reg_same_as_src =
+ (rl_src.location == kLocPhysReg && rl_src.reg.GetRegNum() == rl_result.reg.GetRegNum());
+ const bool true_zero_case = (true_val == 0 && false_val != 0 && !result_reg_same_as_src);
+ const bool false_zero_case = (false_val == 0 && true_val != 0 && !result_reg_same_as_src);
+ const bool catch_all_case = !(true_zero_case || false_zero_case);
- if (true_zero_case || false_zero_case) {
- OpRegReg(kOpXor, rl_result.reg, rl_result.reg);
- }
+ if (true_zero_case || false_zero_case) {
+ OpRegReg(kOpXor, rl_result.reg, rl_result.reg);
+ }
- if (true_zero_case || false_zero_case || catch_all_case) {
- OpRegImm(kOpCmp, rl_src.reg, 0);
- }
+ if (true_zero_case || false_zero_case || catch_all_case) {
+ OpRegImm(kOpCmp, rl_src.reg, 0);
+ }
- if (catch_all_case) {
- OpRegImm(kOpMov, rl_result.reg, false_val);
- }
+ if (catch_all_case) {
+ OpRegImm(kOpMov, rl_result.reg, false_val);
+ }
- if (true_zero_case || false_zero_case || catch_all_case) {
- ConditionCode cc = true_zero_case ? NegateComparison(ccode) : ccode;
- int immediateForTemp = true_zero_case ? false_val : true_val;
- RegStorage temp1_reg = AllocTypedTemp(false, result_reg_class);
- OpRegImm(kOpMov, temp1_reg, immediateForTemp);
+ if (true_zero_case || false_zero_case || catch_all_case) {
+ ConditionCode cc = true_zero_case ? NegateComparison(ccode) : ccode;
+ int immediateForTemp = true_zero_case ? false_val : true_val;
+ RegStorage temp1_reg = AllocTypedTemp(false, result_reg_class);
+ OpRegImm(kOpMov, temp1_reg, immediateForTemp);
- OpCondRegReg(kOpCmov, cc, rl_result.reg, temp1_reg);
+ OpCondRegReg(kOpCmov, cc, rl_result.reg, temp1_reg);
- FreeTemp(temp1_reg);
+ FreeTemp(temp1_reg);
+ }
}
} else {
+ rl_src = LoadValue(rl_src, src_reg_class);
RegLocation rl_true = mir_graph_->GetSrc(mir, 1);
RegLocation rl_false = mir_graph_->GetSrc(mir, 2);
rl_true = LoadValue(rl_true, result_reg_class);
@@ -398,8 +407,8 @@
return;
}
- FlushAllRegs();
- LockCallTemps(); // Prepare for explicit register usage
+ // Prepare for explicit register usage
+ ExplicitTempRegisterLock(this, 4, &rs_r0, &rs_r1, &rs_r2, &rs_r3);
RegStorage r_tmp1 = RegStorage::MakeRegPair(rs_r0, rs_r1);
RegStorage r_tmp2 = RegStorage::MakeRegPair(rs_r2, rs_r3);
LoadValueDirectWideFixed(rl_src1, r_tmp1);
@@ -683,33 +692,27 @@
Clobber(rs_r2);
LockTemp(rs_r2);
- // Assume that the result will be in EDX.
- rl_result = {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, rs_r2, INVALID_SREG, INVALID_SREG};
+ // Assume that the result will be in EDX for divide, and EAX for remainder.
+ rl_result = {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, is_div ? rs_r2 : rs_r0,
+ INVALID_SREG, INVALID_SREG};
- // Numerator into EAX.
- RegStorage numerator_reg;
- if (!is_div || (imm > 0 && magic < 0) || (imm < 0 && magic > 0)) {
- // We will need the value later.
- rl_src = LoadValue(rl_src, kCoreReg);
- numerator_reg = rl_src.reg;
- OpRegCopy(rs_r0, numerator_reg);
- } else {
- // Only need this once. Just put it into EAX.
- LoadValueDirectFixed(rl_src, rs_r0);
- }
+ // We need the value at least twice. Load into a temp.
+ rl_src = LoadValue(rl_src, kCoreReg);
+ RegStorage numerator_reg = rl_src.reg;
- // Check if numerator is 0
- OpRegImm(kOpCmp, rs_r0, 0);
+ // Check if numerator is 0.
+ OpRegImm(kOpCmp, numerator_reg, 0);
LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
- LoadConstantNoClobber(rs_r2, 0);
+ // Return result 0 if numerator was 0.
+ LoadConstantNoClobber(rl_result.reg, 0);
LIR* done = NewLIR1(kX86Jmp8, 0);
branch->target = NewLIR0(kPseudoTargetLabel);
- // EDX = magic.
- LoadConstantNoClobber(rs_r2, magic);
+ // EAX = magic.
+ LoadConstant(rs_r0, magic);
- // EDX:EAX = magic & dividend.
- NewLIR1(kX86Imul32DaR, rs_r2.GetReg());
+ // EDX:EAX = magic * numerator.
+ NewLIR1(kX86Imul32DaR, numerator_reg.GetReg());
if (imm > 0 && magic < 0) {
// Add numerator to EDX.
@@ -747,11 +750,10 @@
// EAX = numerator * imm.
OpRegRegImm(kOpMul, rs_r2, rs_r2, imm);
- // EDX -= EAX.
+ // EAX -= EDX.
NewLIR2(kX86Sub32RR, rs_r0.GetReg(), rs_r2.GetReg());
// For this case, return the result in EAX.
- rl_result.reg.SetReg(r0);
}
done->target = NewLIR0(kPseudoTargetLabel);
}
@@ -768,8 +770,9 @@
RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2, bool is_div, bool check_zero) {
// We have to use fixed registers, so flush all the temps.
- FlushAllRegs();
- LockCallTemps(); // Prepare for explicit register usage.
+
+ // Prepare for explicit register usage.
+ ExplicitTempRegisterLock(this, 3, &rs_r0, &rs_r1, &rs_r2);
// Load LHS into EAX.
LoadValueDirectFixed(rl_src1, rs_r0);
@@ -791,11 +794,11 @@
// Have to catch 0x80000000/-1 case, or we will get an exception!
OpRegImm(kOpCmp, rs_r1, -1);
- LIR *minus_one_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+ LIR* minus_one_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
// RHS is -1.
OpRegImm(kOpCmp, rs_r0, 0x80000000);
- LIR * minint_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+ LIR* minint_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
branch->target = NewLIR0(kPseudoTargetLabel);
@@ -1606,8 +1609,8 @@
if (!cu_->target64) {
int32_t val_lo = Low32Bits(val);
int32_t val_hi = High32Bits(val);
- FlushAllRegs();
- LockCallTemps(); // Prepare for explicit register usage.
+ // Prepare for explicit register usage.
+ ExplicitTempRegisterLock(this, 3, &rs_r0, &rs_r1, &rs_r2);
rl_src1 = UpdateLocWideTyped(rl_src1, kCoreReg);
bool src1_in_reg = rl_src1.location == kLocPhysReg;
int displacement = SRegOffset(rl_src1.s_reg_low);
@@ -1690,8 +1693,8 @@
bool is_square = mir_graph_->SRegToVReg(rl_src1.s_reg_low) ==
mir_graph_->SRegToVReg(rl_src2.s_reg_low);
- FlushAllRegs();
- LockCallTemps(); // Prepare for explicit register usage.
+ // Prepare for explicit register usage.
+ ExplicitTempRegisterLock(this, 3, &rs_r0, &rs_r1, &rs_r2);
rl_src1 = UpdateLocWideTyped(rl_src1, kCoreReg);
rl_src2 = UpdateLocWideTyped(rl_src2, kCoreReg);
@@ -1714,7 +1717,7 @@
NewLIR2(kX86Imul32RR, rs_r1.GetReg(), rl_src2.reg.GetLowReg());
} else {
int displacement = SRegOffset(rl_src2.s_reg_low);
- LIR *m = NewLIR3(kX86Imul32RM, rs_r1.GetReg(), rs_rX86_SP.GetReg(),
+ LIR* m = NewLIR3(kX86Imul32RM, rs_r1.GetReg(), rs_rX86_SP.GetReg(),
displacement + LOWORD_OFFSET);
AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2,
true /* is_load */, true /* is_64bit */);
@@ -2035,7 +2038,8 @@
Clobber(rs_r2q);
LockTemp(rs_r2q);
- RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, rs_r2q, INVALID_SREG, INVALID_SREG};
+ RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1,
+ is_div ? rs_r2q : rs_r0q, INVALID_SREG, INVALID_SREG};
// Use H.S.Warren's Hacker's Delight Chapter 10 and
// T,Grablund, P.L.Montogomery's Division by invariant integers using multiplication.
@@ -2059,24 +2063,35 @@
* 5. Thus, RDX is the quotient
*/
- // Numerator into RAX.
+ // RAX = magic.
+ LoadConstantWide(rs_r0q, magic);
+
+ // Multiply by numerator.
RegStorage numerator_reg;
if (!is_div || (imm > 0 && magic < 0) || (imm < 0 && magic > 0)) {
// We will need the value later.
rl_src = LoadValueWide(rl_src, kCoreReg);
numerator_reg = rl_src.reg;
- OpRegCopyWide(rs_r0q, numerator_reg);
+
+ // RDX:RAX = magic * numerator.
+ NewLIR1(kX86Imul64DaR, numerator_reg.GetReg());
} else {
- // Only need this once. Just put it into RAX.
- LoadValueDirectWideFixed(rl_src, rs_r0q);
+ // Only need this once. Multiply directly from the value.
+ rl_src = UpdateLocWideTyped(rl_src, kCoreReg);
+ if (rl_src.location != kLocPhysReg) {
+ // Okay, we can do this from memory.
+ ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+ int displacement = SRegOffset(rl_src.s_reg_low);
+ // RDX:RAX = magic * numerator.
+ LIR *m = NewLIR2(kX86Imul64DaM, rs_rX86_SP.GetReg(), displacement);
+ AnnotateDalvikRegAccess(m, displacement >> 2,
+ true /* is_load */, true /* is_64bit */);
+ } else {
+ // RDX:RAX = magic * numerator.
+ NewLIR1(kX86Imul64DaR, rl_src.reg.GetReg());
+ }
}
- // RDX = magic.
- LoadConstantWide(rs_r2q, magic);
-
- // RDX:RAX = magic & dividend.
- NewLIR1(kX86Imul64DaR, rs_r2q.GetReg());
-
if (imm > 0 && magic < 0) {
// Add numerator to RDX.
DCHECK(numerator_reg.Valid());
@@ -2124,14 +2139,12 @@
NewLIR3(kX86Imul64RRI, rs_r2q.GetReg(), rs_r2q.GetReg(), short_imm);
}
- // RDX -= RAX.
+ // RAX -= RDX.
OpRegReg(kOpSub, rs_r0q, rs_r2q);
- // Store result.
- OpRegCopyWide(rl_result.reg, rs_r0q);
+ // Result in RAX.
} else {
- // Store result.
- OpRegCopyWide(rl_result.reg, rs_r2q);
+ // Result in RDX.
}
StoreValueWide(rl_dest, rl_result);
FreeTemp(rs_r0q);
@@ -2154,8 +2167,8 @@
}
// We have to use fixed registers, so flush all the temps.
- FlushAllRegs();
- LockCallTemps(); // Prepare for explicit register usage.
+ // Prepare for explicit register usage.
+ ExplicitTempRegisterLock(this, 4, &rs_r0q, &rs_r1q, &rs_r2q, &rs_r6q);
// Load LHS into RAX.
LoadValueDirectWideFixed(rl_src1, rs_r0q);
@@ -2171,7 +2184,7 @@
// Have to catch 0x8000000000000000/-1 case, or we will get an exception!
NewLIR2(kX86Cmp64RI8, rs_r1q.GetReg(), -1);
- LIR *minus_one_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+ LIR* minus_one_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
// RHS is -1.
LoadConstantWide(rs_r6q, 0x8000000000000000);
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index ffe6702..aadb41a 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#include <string>
+#include <cstdarg>
#include <inttypes.h>
+#include <string>
#include "backend_x86.h"
#include "codegen_x86.h"
@@ -2919,4 +2920,46 @@
return true;
}
+/**
+ * Lock temp registers for explicit usage. Registers will be freed in destructor.
+ */
+X86Mir2Lir::ExplicitTempRegisterLock::ExplicitTempRegisterLock(X86Mir2Lir* mir_to_lir,
+ int n_regs, ...) :
+ temp_regs_(n_regs),
+ mir_to_lir_(mir_to_lir) {
+ va_list regs;
+ va_start(regs, n_regs);
+ for (int i = 0; i < n_regs; i++) {
+ RegStorage reg = *(va_arg(regs, RegStorage*));
+ RegisterInfo* info = mir_to_lir_->GetRegInfo(reg);
+
+ // Make sure we don't have promoted register here.
+ DCHECK(info->IsTemp());
+
+ temp_regs_.push_back(reg);
+ mir_to_lir_->FlushReg(reg);
+
+ if (reg.IsPair()) {
+ RegStorage partner = info->Partner();
+ temp_regs_.push_back(partner);
+ mir_to_lir_->FlushReg(partner);
+ }
+
+ mir_to_lir_->Clobber(reg);
+ mir_to_lir_->LockTemp(reg);
+ }
+
+ va_end(regs);
+}
+
+/*
+ * Free all locked registers.
+ */
+X86Mir2Lir::ExplicitTempRegisterLock::~ExplicitTempRegisterLock() {
+ // Free all locked temps.
+ for (auto it : temp_regs_) {
+ mir_to_lir_->FreeTemp(it);
+ }
+}
+
} // namespace art
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
new file mode 100644
index 0000000..35320f5
--- /dev/null
+++ b/compiler/elf_builder.h
@@ -0,0 +1,1069 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_ELF_BUILDER_H_
+#define ART_COMPILER_ELF_BUILDER_H_
+
+#include "buffered_output_stream.h"
+#include "elf_utils.h"
+#include "file_output_stream.h"
+#include "instruction_set.h"
+
+namespace art {
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfSectionBuilder {
+ public:
+ ElfSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
+ const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *link, Elf_Word info,
+ Elf_Word align, Elf_Word entsize) : name_(sec_name), link_(link) {
+ memset(§ion_, 0, sizeof(section_));
+ section_.sh_type = type;
+ section_.sh_flags = flags;
+ section_.sh_info = info;
+ section_.sh_addralign = align;
+ section_.sh_entsize = entsize;
+ }
+
+ virtual ~ElfSectionBuilder() {}
+
+ Elf_Shdr section_;
+ Elf_Word section_index_ = 0;
+
+ Elf_Word GetLink() {
+ return (link_) ? link_->section_index_ : 0;
+ }
+
+ const std::string name_;
+
+ protected:
+ const ElfSectionBuilder* link_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Dyn, typename Elf_Shdr>
+class ElfDynamicBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+ void AddDynamicTag(Elf_Sword tag, Elf_Word d_un) {
+ if (tag == DT_NULL) {
+ return;
+ }
+ dynamics_.push_back({nullptr, tag, d_un});
+ }
+
+ void AddDynamicTag(Elf_Sword tag, Elf_Word d_un,
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section) {
+ if (tag == DT_NULL) {
+ return;
+ }
+ dynamics_.push_back({section, tag, d_un});
+ }
+
+ ElfDynamicBuilder(const std::string& sec_name,
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *link)
+ : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, SHT_DYNAMIC, SHF_ALLOC | SHF_ALLOC,
+ link, 0, kPageSize, sizeof(Elf_Dyn)) {}
+ ~ElfDynamicBuilder() {}
+
+ Elf_Word GetSize() {
+ // Add 1 for the DT_NULL, 1 for DT_STRSZ, and 1 for DT_SONAME. All of
+ // these must be added when we actually put the file together because
+ // their values are very dependent on state.
+ return dynamics_.size() + 3;
+ }
+
+ // Create the actual dynamic vector. strsz should be the size of the .dynstr
+ // table and soname_off should be the offset of the soname in .dynstr.
+ // Since niether can be found prior to final layout we will wait until here
+ // to add them.
+ std::vector<Elf_Dyn> GetDynamics(Elf_Word strsz, Elf_Word soname) {
+ std::vector<Elf_Dyn> ret;
+ for (auto it = dynamics_.cbegin(); it != dynamics_.cend(); ++it) {
+ if (it->section_) {
+ // We are adding an address relative to a section.
+ ret.push_back(
+ {it->tag_, {it->off_ + it->section_->section_.sh_addr}});
+ } else {
+ ret.push_back({it->tag_, {it->off_}});
+ }
+ }
+ ret.push_back({DT_STRSZ, {strsz}});
+ ret.push_back({DT_SONAME, {soname}});
+ ret.push_back({DT_NULL, {0}});
+ return ret;
+ }
+
+ protected:
+ struct ElfDynamicState {
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section_;
+ Elf_Sword tag_;
+ Elf_Word off_;
+ };
+ std::vector<ElfDynamicState> dynamics_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfRawSectionBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+ ElfRawSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
+ const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* link, Elf_Word info,
+ Elf_Word align, Elf_Word entsize)
+ : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, flags, link, info, align,
+ entsize) {}
+ ~ElfRawSectionBuilder() {}
+ std::vector<uint8_t>* GetBuffer() { return &buf_; }
+ void SetBuffer(std::vector<uint8_t>&& buf) { buf_ = buf; }
+
+ protected:
+ std::vector<uint8_t> buf_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfOatSectionBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+ ElfOatSectionBuilder(const std::string& sec_name, Elf_Word size, Elf_Word offset,
+ Elf_Word type, Elf_Word flags)
+ : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, flags, nullptr, 0, kPageSize,
+ 0), offset_(offset), size_(size) {}
+ ~ElfOatSectionBuilder() {}
+
+ Elf_Word GetOffset() {
+ return offset_;
+ }
+
+ Elf_Word GetSize() {
+ return size_;
+ }
+
+ protected:
+ // Offset of the content within the file.
+ Elf_Word offset_;
+ // Size of the content within the file.
+ Elf_Word size_;
+};
+
+static inline constexpr uint8_t MakeStInfo(uint8_t binding, uint8_t type) {
+ return ((binding) << 4) + ((type) & 0xf);
+}
+
+// from bionic
+static inline unsigned elfhash(const char *_name) {
+ const unsigned char *name = (const unsigned char *) _name;
+ unsigned h = 0, g;
+
+ while (*name) {
+ h = (h << 4) + *name++;
+ g = h & 0xf0000000;
+ h ^= g;
+ h ^= g >> 24;
+ }
+ return h;
+}
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Sym,
+ typename Elf_Shdr>
+class ElfSymtabBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+ // Add a symbol with given name to this symtab. The symbol refers to
+ // 'relative_addr' within the given section and has the given attributes.
+ void AddSymbol(const std::string& name,
+ const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section,
+ Elf_Addr addr,
+ bool is_relative,
+ Elf_Word size,
+ uint8_t binding,
+ uint8_t type,
+ uint8_t other = 0) {
+ CHECK(section);
+ ElfSymtabBuilder::ElfSymbolState state {name, section, addr, size, is_relative,
+ MakeStInfo(binding, type), other, 0};
+ symbols_.push_back(state);
+ }
+
+ ElfSymtabBuilder(const std::string& sec_name, Elf_Word type,
+ const std::string& str_name, Elf_Word str_type, bool alloc)
+ : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, ((alloc) ? SHF_ALLOC : 0U),
+ &strtab_, 0, sizeof(Elf_Word),
+ sizeof(Elf_Sym)), str_name_(str_name),
+ str_type_(str_type),
+ strtab_(str_name,
+ str_type,
+ ((alloc) ? SHF_ALLOC : 0U),
+ nullptr, 0, 1, 1) {}
+ ~ElfSymtabBuilder() {}
+
+ std::vector<Elf_Word> GenerateHashContents() {
+ // Here is how The ELF hash table works.
+ // There are 3 arrays to worry about.
+ // * The symbol table where the symbol information is.
+ // * The bucket array which is an array of indexes into the symtab and chain.
+ // * The chain array which is also an array of indexes into the symtab and chain.
+ //
+ // Lets say the state is something like this.
+ // +--------+ +--------+ +-----------+
+ // | symtab | | bucket | | chain |
+ // | null | | 1 | | STN_UNDEF |
+ // | <sym1> | | 4 | | 2 |
+ // | <sym2> | | | | 5 |
+ // | <sym3> | | | | STN_UNDEF |
+ // | <sym4> | | | | 3 |
+ // | <sym5> | | | | STN_UNDEF |
+ // +--------+ +--------+ +-----------+
+ //
+ // The lookup process (in python psudocode) is
+ //
+ // def GetSym(name):
+ // # NB STN_UNDEF == 0
+ // indx = bucket[elfhash(name) % num_buckets]
+ // while indx != STN_UNDEF:
+ // if GetSymbolName(symtab[indx]) == name:
+ // return symtab[indx]
+ // indx = chain[indx]
+ // return SYMBOL_NOT_FOUND
+ //
+ // Between bucket and chain arrays every symtab index must be present exactly
+ // once (except for STN_UNDEF, which must be present 1 + num_bucket times).
+
+ // Select number of buckets.
+ // This is essentially arbitrary.
+ Elf_Word nbuckets;
+ Elf_Word chain_size = GetSize();
+ if (symbols_.size() < 8) {
+ nbuckets = 2;
+ } else if (symbols_.size() < 32) {
+ nbuckets = 4;
+ } else if (symbols_.size() < 256) {
+ nbuckets = 16;
+ } else {
+ // Have about 32 ids per bucket.
+ nbuckets = RoundUp(symbols_.size()/32, 2);
+ }
+ std::vector<Elf_Word> hash;
+ hash.push_back(nbuckets);
+ hash.push_back(chain_size);
+ uint32_t bucket_offset = hash.size();
+ uint32_t chain_offset = bucket_offset + nbuckets;
+ hash.resize(hash.size() + nbuckets + chain_size, 0);
+
+ Elf_Word* buckets = hash.data() + bucket_offset;
+ Elf_Word* chain = hash.data() + chain_offset;
+
+ // Set up the actual hash table.
+ for (Elf_Word i = 0; i < symbols_.size(); i++) {
+ // Add 1 since we need to have the null symbol that is not in the symbols
+ // list.
+ Elf_Word index = i + 1;
+ Elf_Word hash_val = static_cast<Elf_Word>(elfhash(symbols_[i].name_.c_str())) % nbuckets;
+ if (buckets[hash_val] == 0) {
+ buckets[hash_val] = index;
+ } else {
+ hash_val = buckets[hash_val];
+ CHECK_LT(hash_val, chain_size);
+ while (chain[hash_val] != 0) {
+ hash_val = chain[hash_val];
+ CHECK_LT(hash_val, chain_size);
+ }
+ chain[hash_val] = index;
+ // Check for loops. Works because if this is non-empty then there must be
+ // another cell which already contains the same symbol index as this one,
+ // which means some symbol has more then one name, which isn't allowed.
+ CHECK_EQ(chain[index], static_cast<Elf_Word>(0));
+ }
+ }
+
+ return hash;
+ }
+
+ std::string GenerateStrtab() {
+ std::string tab;
+ tab += '\0';
+ for (auto it = symbols_.begin(); it != symbols_.end(); ++it) {
+ it->name_idx_ = tab.size();
+ tab += it->name_;
+ tab += '\0';
+ }
+ strtab_.section_.sh_size = tab.size();
+ return tab;
+ }
+
+ std::vector<Elf_Sym> GenerateSymtab() {
+ std::vector<Elf_Sym> ret;
+ Elf_Sym undef_sym;
+ memset(&undef_sym, 0, sizeof(undef_sym));
+ undef_sym.st_shndx = SHN_UNDEF;
+ ret.push_back(undef_sym);
+
+ for (auto it = symbols_.cbegin(); it != symbols_.cend(); ++it) {
+ Elf_Sym sym;
+ memset(&sym, 0, sizeof(sym));
+ sym.st_name = it->name_idx_;
+ if (it->is_relative_) {
+ sym.st_value = it->addr_ + it->section_->section_.sh_offset;
+ } else {
+ sym.st_value = it->addr_;
+ }
+ sym.st_size = it->size_;
+ sym.st_other = it->other_;
+ sym.st_shndx = it->section_->section_index_;
+ sym.st_info = it->info_;
+
+ ret.push_back(sym);
+ }
+ return ret;
+ }
+
+ Elf_Word GetSize() {
+ // 1 is for the implicit NULL symbol.
+ return symbols_.size() + 1;
+ }
+
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* GetStrTab() {
+ return &strtab_;
+ }
+
+ protected:
+ struct ElfSymbolState {
+ const std::string name_;
+ const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section_;
+ Elf_Addr addr_;
+ Elf_Word size_;
+ bool is_relative_;
+ uint8_t info_;
+ uint8_t other_;
+ // Used during Write() to temporarially hold name index in the strtab.
+ Elf_Word name_idx_;
+ };
+
+ // Information for the strsym for dynstr sections.
+ const std::string str_name_;
+ Elf_Word str_type_;
+ // The symbols in the same order they will be in the symbol table.
+ std::vector<ElfSymbolState> symbols_;
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> strtab_;
+};
+
+class CodeOutput {
+ public:
+ virtual bool Write(OutputStream* out) = 0;
+ virtual ~CodeOutput() {}
+};
+
+template <typename Elf_Word, typename Elf_Shdr>
+static inline constexpr Elf_Word NextOffset(const Elf_Shdr& cur, const Elf_Shdr& prev) {
+ return RoundUp(prev.sh_size + prev.sh_offset, cur.sh_addralign);
+}
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Dyn,
+ typename Elf_Sym, typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr>
+class ElfBuilder FINAL {
+ public:
+ ElfBuilder(CodeOutput* oat_writer,
+ File* elf_file,
+ InstructionSet isa,
+ Elf_Word rodata_relative_offset,
+ Elf_Word rodata_size,
+ Elf_Word text_relative_offset,
+ Elf_Word text_size,
+ const bool add_symbols,
+ bool debug = false)
+ : oat_writer_(oat_writer),
+ elf_file_(elf_file),
+ add_symbols_(add_symbols),
+ debug_logging_(debug),
+ text_builder_(".text", text_size, text_relative_offset, SHT_PROGBITS,
+ SHF_ALLOC | SHF_EXECINSTR),
+ rodata_builder_(".rodata", rodata_size, rodata_relative_offset, SHT_PROGBITS, SHF_ALLOC),
+ dynsym_builder_(".dynsym", SHT_DYNSYM, ".dynstr", SHT_STRTAB, true),
+ symtab_builder_(".symtab", SHT_SYMTAB, ".strtab", SHT_STRTAB, false),
+ hash_builder_(".hash", SHT_HASH, SHF_ALLOC, &dynsym_builder_, 0, sizeof(Elf_Word),
+ sizeof(Elf_Word)),
+ dynamic_builder_(".dynamic", &dynsym_builder_),
+ shstrtab_builder_(".shstrtab", SHT_STRTAB, 0, NULL, 0, 1, 1) {
+ SetupEhdr();
+ SetupDynamic();
+ SetupRequiredSymbols();
+ SetISA(isa);
+ }
+ ~ElfBuilder() {}
+
+ bool Init() {
+ // The basic layout of the elf file. Order may be different in final output.
+ // +-------------------------+
+ // | Elf_Ehdr |
+ // +-------------------------+
+ // | Elf_Phdr PHDR |
+ // | Elf_Phdr LOAD R | .dynsym .dynstr .hash .rodata
+ // | Elf_Phdr LOAD R X | .text
+ // | Elf_Phdr LOAD RW | .dynamic
+ // | Elf_Phdr DYNAMIC | .dynamic
+ // +-------------------------+
+ // | .dynsym |
+ // | Elf_Sym STN_UNDEF |
+ // | Elf_Sym oatdata |
+ // | Elf_Sym oatexec |
+ // | Elf_Sym oatlastword |
+ // +-------------------------+
+ // | .dynstr |
+ // | \0 |
+ // | oatdata\0 |
+ // | oatexec\0 |
+ // | oatlastword\0 |
+ // | boot.oat\0 |
+ // +-------------------------+
+ // | .hash |
+ // | Elf_Word nbucket = b |
+ // | Elf_Word nchain = c |
+ // | Elf_Word bucket[0] |
+ // | ... |
+ // | Elf_Word bucket[b - 1] |
+ // | Elf_Word chain[0] |
+ // | ... |
+ // | Elf_Word chain[c - 1] |
+ // +-------------------------+
+ // | .rodata |
+ // | oatdata..oatexec-4 |
+ // +-------------------------+
+ // | .text |
+ // | oatexec..oatlastword |
+ // +-------------------------+
+ // | .dynamic |
+ // | Elf_Dyn DT_SONAME |
+ // | Elf_Dyn DT_HASH |
+ // | Elf_Dyn DT_SYMTAB |
+ // | Elf_Dyn DT_SYMENT |
+ // | Elf_Dyn DT_STRTAB |
+ // | Elf_Dyn DT_STRSZ |
+ // | Elf_Dyn DT_NULL |
+ // +-------------------------+ (Optional)
+ // | .strtab | (Optional)
+ // | program symbol names | (Optional)
+ // +-------------------------+ (Optional)
+ // | .symtab | (Optional)
+ // | program symbols | (Optional)
+ // +-------------------------+
+ // | .shstrtab |
+ // | \0 |
+ // | .dynamic\0 |
+ // | .dynsym\0 |
+ // | .dynstr\0 |
+ // | .hash\0 |
+ // | .rodata\0 |
+ // | .text\0 |
+ // | .shstrtab\0 |
+ // | .symtab\0 | (Optional)
+ // | .strtab\0 | (Optional)
+ // | .debug_str\0 | (Optional)
+ // | .debug_info\0 | (Optional)
+ // | .eh_frame\0 | (Optional)
+ // | .debug_line\0 | (Optional)
+ // | .debug_abbrev\0 | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_info | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_abbrev | (Optional)
+ // +-------------------------+ (Optional)
+ // | .eh_frame | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_line | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_str | (Optional)
+ // +-------------------------+ (Optional)
+ // | Elf_Shdr NULL |
+ // | Elf_Shdr .dynsym |
+ // | Elf_Shdr .dynstr |
+ // | Elf_Shdr .hash |
+ // | Elf_Shdr .text |
+ // | Elf_Shdr .rodata |
+ // | Elf_Shdr .dynamic |
+ // | Elf_Shdr .shstrtab |
+ // | Elf_Shdr .debug_info | (Optional)
+ // | Elf_Shdr .debug_abbrev | (Optional)
+ // | Elf_Shdr .eh_frame | (Optional)
+ // | Elf_Shdr .debug_line | (Optional)
+ // | Elf_Shdr .debug_str | (Optional)
+ // +-------------------------+
+
+ if (fatal_error_) {
+ return false;
+ }
+ // Step 1. Figure out all the offsets.
+
+ if (debug_logging_) {
+ LOG(INFO) << "phdr_offset=" << PHDR_OFFSET << std::hex << " " << PHDR_OFFSET;
+ LOG(INFO) << "phdr_size=" << PHDR_SIZE << std::hex << " " << PHDR_SIZE;
+ }
+
+ memset(&program_headers_, 0, sizeof(program_headers_));
+ program_headers_[PH_PHDR].p_type = PT_PHDR;
+ program_headers_[PH_PHDR].p_offset = PHDR_OFFSET;
+ program_headers_[PH_PHDR].p_vaddr = PHDR_OFFSET;
+ program_headers_[PH_PHDR].p_paddr = PHDR_OFFSET;
+ program_headers_[PH_PHDR].p_filesz = sizeof(program_headers_);
+ program_headers_[PH_PHDR].p_memsz = sizeof(program_headers_);
+ program_headers_[PH_PHDR].p_flags = PF_R;
+ program_headers_[PH_PHDR].p_align = sizeof(Elf_Word);
+
+ program_headers_[PH_LOAD_R__].p_type = PT_LOAD;
+ program_headers_[PH_LOAD_R__].p_offset = 0;
+ program_headers_[PH_LOAD_R__].p_vaddr = 0;
+ program_headers_[PH_LOAD_R__].p_paddr = 0;
+ program_headers_[PH_LOAD_R__].p_flags = PF_R;
+
+ program_headers_[PH_LOAD_R_X].p_type = PT_LOAD;
+ program_headers_[PH_LOAD_R_X].p_flags = PF_R | PF_X;
+
+ program_headers_[PH_LOAD_RW_].p_type = PT_LOAD;
+ program_headers_[PH_LOAD_RW_].p_flags = PF_R | PF_W;
+
+ program_headers_[PH_DYNAMIC].p_type = PT_DYNAMIC;
+ program_headers_[PH_DYNAMIC].p_flags = PF_R | PF_W;
+
+ // Get the dynstr string.
+ dynstr_ = dynsym_builder_.GenerateStrtab();
+
+ // Add the SONAME to the dynstr.
+ dynstr_soname_offset_ = dynstr_.size();
+ std::string file_name(elf_file_->GetPath());
+ size_t directory_separator_pos = file_name.rfind('/');
+ if (directory_separator_pos != std::string::npos) {
+ file_name = file_name.substr(directory_separator_pos + 1);
+ }
+ dynstr_ += file_name;
+ dynstr_ += '\0';
+ if (debug_logging_) {
+ LOG(INFO) << "dynstr size (bytes) =" << dynstr_.size()
+ << std::hex << " " << dynstr_.size();
+ LOG(INFO) << "dynsym size (elements)=" << dynsym_builder_.GetSize()
+ << std::hex << " " << dynsym_builder_.GetSize();
+ }
+
+ // Get the section header string table.
+ shstrtab_ += '\0';
+
+ // Setup sym_undef
+ memset(&null_hdr_, 0, sizeof(null_hdr_));
+ null_hdr_.sh_type = SHT_NULL;
+ null_hdr_.sh_link = SHN_UNDEF;
+ section_ptrs_.push_back(&null_hdr_);
+
+ section_index_ = 1;
+
+ // setup .dynsym
+ section_ptrs_.push_back(&dynsym_builder_.section_);
+ AssignSectionStr(&dynsym_builder_, &shstrtab_);
+ dynsym_builder_.section_index_ = section_index_++;
+
+ // Setup .dynstr
+ section_ptrs_.push_back(&dynsym_builder_.GetStrTab()->section_);
+ AssignSectionStr(dynsym_builder_.GetStrTab(), &shstrtab_);
+ dynsym_builder_.GetStrTab()->section_index_ = section_index_++;
+
+ // Setup .hash
+ section_ptrs_.push_back(&hash_builder_.section_);
+ AssignSectionStr(&hash_builder_, &shstrtab_);
+ hash_builder_.section_index_ = section_index_++;
+
+ // Setup .rodata
+ section_ptrs_.push_back(&rodata_builder_.section_);
+ AssignSectionStr(&rodata_builder_, &shstrtab_);
+ rodata_builder_.section_index_ = section_index_++;
+
+ // Setup .text
+ section_ptrs_.push_back(&text_builder_.section_);
+ AssignSectionStr(&text_builder_, &shstrtab_);
+ text_builder_.section_index_ = section_index_++;
+
+ // Setup .dynamic
+ section_ptrs_.push_back(&dynamic_builder_.section_);
+ AssignSectionStr(&dynamic_builder_, &shstrtab_);
+ dynamic_builder_.section_index_ = section_index_++;
+
+ // Fill in the hash section.
+ hash_ = dynsym_builder_.GenerateHashContents();
+
+ if (debug_logging_) {
+ LOG(INFO) << ".hash size (bytes)=" << hash_.size() * sizeof(Elf_Word)
+ << std::hex << " " << hash_.size() * sizeof(Elf_Word);
+ }
+
+ Elf_Word base_offset = sizeof(Elf_Ehdr) + sizeof(program_headers_);
+
+ // Get the layout in the sections.
+ //
+ // Get the layout of the dynsym section.
+ dynsym_builder_.section_.sh_offset = RoundUp(base_offset, dynsym_builder_.section_.sh_addralign);
+ dynsym_builder_.section_.sh_addr = dynsym_builder_.section_.sh_offset;
+ dynsym_builder_.section_.sh_size = dynsym_builder_.GetSize() * sizeof(Elf_Sym);
+ dynsym_builder_.section_.sh_link = dynsym_builder_.GetLink();
+
+ // Get the layout of the dynstr section.
+ dynsym_builder_.GetStrTab()->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (dynsym_builder_.GetStrTab()->section_,
+ dynsym_builder_.section_);
+ dynsym_builder_.GetStrTab()->section_.sh_addr = dynsym_builder_.GetStrTab()->section_.sh_offset;
+ dynsym_builder_.GetStrTab()->section_.sh_size = dynstr_.size();
+ dynsym_builder_.GetStrTab()->section_.sh_link = dynsym_builder_.GetStrTab()->GetLink();
+
+ // Get the layout of the hash section
+ hash_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (hash_builder_.section_,
+ dynsym_builder_.GetStrTab()->section_);
+ hash_builder_.section_.sh_addr = hash_builder_.section_.sh_offset;
+ hash_builder_.section_.sh_size = hash_.size() * sizeof(Elf_Word);
+ hash_builder_.section_.sh_link = hash_builder_.GetLink();
+
+ // Get the layout of the rodata section.
+ rodata_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (rodata_builder_.section_,
+ hash_builder_.section_);
+ rodata_builder_.section_.sh_addr = rodata_builder_.section_.sh_offset;
+ rodata_builder_.section_.sh_size = rodata_builder_.GetSize();
+ rodata_builder_.section_.sh_link = rodata_builder_.GetLink();
+
+ // Get the layout of the text section.
+ text_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (text_builder_.section_, rodata_builder_.section_);
+ text_builder_.section_.sh_addr = text_builder_.section_.sh_offset;
+ text_builder_.section_.sh_size = text_builder_.GetSize();
+ text_builder_.section_.sh_link = text_builder_.GetLink();
+ CHECK_ALIGNED(rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size, kPageSize);
+
+ // Get the layout of the dynamic section.
+ dynamic_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (dynamic_builder_.section_,
+ text_builder_.section_);
+ dynamic_builder_.section_.sh_addr = dynamic_builder_.section_.sh_offset;
+ dynamic_builder_.section_.sh_size = dynamic_builder_.GetSize() * sizeof(Elf_Dyn);
+ dynamic_builder_.section_.sh_link = dynamic_builder_.GetLink();
+
+ if (debug_logging_) {
+ LOG(INFO) << "dynsym off=" << dynsym_builder_.section_.sh_offset
+ << " dynsym size=" << dynsym_builder_.section_.sh_size;
+ LOG(INFO) << "dynstr off=" << dynsym_builder_.GetStrTab()->section_.sh_offset
+ << " dynstr size=" << dynsym_builder_.GetStrTab()->section_.sh_size;
+ LOG(INFO) << "hash off=" << hash_builder_.section_.sh_offset
+ << " hash size=" << hash_builder_.section_.sh_size;
+ LOG(INFO) << "rodata off=" << rodata_builder_.section_.sh_offset
+ << " rodata size=" << rodata_builder_.section_.sh_size;
+ LOG(INFO) << "text off=" << text_builder_.section_.sh_offset
+ << " text size=" << text_builder_.section_.sh_size;
+ LOG(INFO) << "dynamic off=" << dynamic_builder_.section_.sh_offset
+ << " dynamic size=" << dynamic_builder_.section_.sh_size;
+ }
+
+ return true;
+ }
+
+ bool Write() {
+ std::vector<ElfFilePiece> pieces;
+ Elf_Shdr prev = dynamic_builder_.section_;
+ std::string strtab;
+
+ if (IncludingDebugSymbols()) {
+ // Setup .symtab
+ section_ptrs_.push_back(&symtab_builder_.section_);
+ AssignSectionStr(&symtab_builder_, &shstrtab_);
+ symtab_builder_.section_index_ = section_index_++;
+
+ // Setup .strtab
+ section_ptrs_.push_back(&symtab_builder_.GetStrTab()->section_);
+ AssignSectionStr(symtab_builder_.GetStrTab(), &shstrtab_);
+ symtab_builder_.GetStrTab()->section_index_ = section_index_++;
+
+ strtab = symtab_builder_.GenerateStrtab();
+ if (debug_logging_) {
+ LOG(INFO) << "strtab size (bytes) =" << strtab.size()
+ << std::hex << " " << strtab.size();
+ LOG(INFO) << "symtab size (elements) =" << symtab_builder_.GetSize()
+ << std::hex << " " << symtab_builder_.GetSize();
+ }
+ }
+
+ // Setup all the other sections.
+ for (ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *builder = other_builders_.data(),
+ *end = builder + other_builders_.size();
+ builder != end; ++builder) {
+ section_ptrs_.push_back(&builder->section_);
+ AssignSectionStr(builder, &shstrtab_);
+ builder->section_index_ = section_index_++;
+ }
+
+ // Setup shstrtab
+ section_ptrs_.push_back(&shstrtab_builder_.section_);
+ AssignSectionStr(&shstrtab_builder_, &shstrtab_);
+ shstrtab_builder_.section_index_ = section_index_++;
+
+ if (debug_logging_) {
+ LOG(INFO) << ".shstrtab size (bytes) =" << shstrtab_.size()
+ << std::hex << " " << shstrtab_.size();
+ LOG(INFO) << "section list size (elements)=" << section_ptrs_.size()
+ << std::hex << " " << section_ptrs_.size();
+ }
+
+ if (IncludingDebugSymbols()) {
+ // Get the layout of the symtab section.
+ symtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (symtab_builder_.section_,
+ dynamic_builder_.section_);
+ symtab_builder_.section_.sh_addr = 0;
+ // Add to leave space for the null symbol.
+ symtab_builder_.section_.sh_size = symtab_builder_.GetSize() * sizeof(Elf_Sym);
+ symtab_builder_.section_.sh_link = symtab_builder_.GetLink();
+
+ // Get the layout of the dynstr section.
+ symtab_builder_.GetStrTab()->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (symtab_builder_.GetStrTab()->section_,
+ symtab_builder_.section_);
+ symtab_builder_.GetStrTab()->section_.sh_addr = 0;
+ symtab_builder_.GetStrTab()->section_.sh_size = strtab.size();
+ symtab_builder_.GetStrTab()->section_.sh_link = symtab_builder_.GetStrTab()->GetLink();
+
+ prev = symtab_builder_.GetStrTab()->section_;
+ if (debug_logging_) {
+ LOG(INFO) << "symtab off=" << symtab_builder_.section_.sh_offset
+ << " symtab size=" << symtab_builder_.section_.sh_size;
+ LOG(INFO) << "strtab off=" << symtab_builder_.GetStrTab()->section_.sh_offset
+ << " strtab size=" << symtab_builder_.GetStrTab()->section_.sh_size;
+ }
+ }
+
+ // Get the layout of the extra sections. (This will deal with the debug
+ // sections if they are there)
+ for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) {
+ it->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>(it->section_, prev);
+ it->section_.sh_addr = 0;
+ it->section_.sh_size = it->GetBuffer()->size();
+ it->section_.sh_link = it->GetLink();
+ pieces.push_back(ElfFilePiece(it->name_, it->section_.sh_offset,
+ it->GetBuffer()->data(), it->GetBuffer()->size()));
+ prev = it->section_;
+ if (debug_logging_) {
+ LOG(INFO) << it->name_ << " off=" << it->section_.sh_offset
+ << " size=" << it->section_.sh_size;
+ }
+ }
+
+ // Get the layout of the shstrtab section
+ shstrtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (shstrtab_builder_.section_, prev);
+ shstrtab_builder_.section_.sh_addr = 0;
+ shstrtab_builder_.section_.sh_size = shstrtab_.size();
+ shstrtab_builder_.section_.sh_link = shstrtab_builder_.GetLink();
+ if (debug_logging_) {
+ LOG(INFO) << "shstrtab off=" << shstrtab_builder_.section_.sh_offset
+ << " shstrtab size=" << shstrtab_builder_.section_.sh_size;
+ }
+
+ // The section list comes after come after.
+ Elf_Word sections_offset = RoundUp(
+ shstrtab_builder_.section_.sh_offset + shstrtab_builder_.section_.sh_size,
+ sizeof(Elf_Word));
+
+ // Setup the actual symbol arrays.
+ std::vector<Elf_Sym> dynsym = dynsym_builder_.GenerateSymtab();
+ CHECK_EQ(dynsym.size() * sizeof(Elf_Sym), dynsym_builder_.section_.sh_size);
+ std::vector<Elf_Sym> symtab;
+ if (IncludingDebugSymbols()) {
+ symtab = symtab_builder_.GenerateSymtab();
+ CHECK_EQ(symtab.size() * sizeof(Elf_Sym), symtab_builder_.section_.sh_size);
+ }
+
+ // Setup the dynamic section.
+ // This will add the 2 values we cannot know until now time, namely the size
+ // and the soname_offset.
+ std::vector<Elf_Dyn> dynamic = dynamic_builder_.GetDynamics(dynstr_.size(),
+ dynstr_soname_offset_);
+ CHECK_EQ(dynamic.size() * sizeof(Elf_Dyn), dynamic_builder_.section_.sh_size);
+
+ // Finish setup of the program headers now that we know the layout of the
+ // whole file.
+ Elf_Word load_r_size = rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size;
+ program_headers_[PH_LOAD_R__].p_filesz = load_r_size;
+ program_headers_[PH_LOAD_R__].p_memsz = load_r_size;
+ program_headers_[PH_LOAD_R__].p_align = rodata_builder_.section_.sh_addralign;
+
+ Elf_Word load_rx_size = text_builder_.section_.sh_size;
+ program_headers_[PH_LOAD_R_X].p_offset = text_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_R_X].p_vaddr = text_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_R_X].p_paddr = text_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_R_X].p_filesz = load_rx_size;
+ program_headers_[PH_LOAD_R_X].p_memsz = load_rx_size;
+ program_headers_[PH_LOAD_R_X].p_align = text_builder_.section_.sh_addralign;
+
+ program_headers_[PH_LOAD_RW_].p_offset = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_RW_].p_vaddr = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_RW_].p_paddr = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_RW_].p_filesz = dynamic_builder_.section_.sh_size;
+ program_headers_[PH_LOAD_RW_].p_memsz = dynamic_builder_.section_.sh_size;
+ program_headers_[PH_LOAD_RW_].p_align = dynamic_builder_.section_.sh_addralign;
+
+ program_headers_[PH_DYNAMIC].p_offset = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_DYNAMIC].p_vaddr = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_DYNAMIC].p_paddr = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_DYNAMIC].p_filesz = dynamic_builder_.section_.sh_size;
+ program_headers_[PH_DYNAMIC].p_memsz = dynamic_builder_.section_.sh_size;
+ program_headers_[PH_DYNAMIC].p_align = dynamic_builder_.section_.sh_addralign;
+
+ // Finish setup of the Ehdr values.
+ elf_header_.e_phoff = PHDR_OFFSET;
+ elf_header_.e_shoff = sections_offset;
+ elf_header_.e_phnum = PH_NUM;
+ elf_header_.e_shnum = section_ptrs_.size();
+ elf_header_.e_shstrndx = shstrtab_builder_.section_index_;
+
+ // Add the rest of the pieces to the list.
+ pieces.push_back(ElfFilePiece("Elf Header", 0, &elf_header_, sizeof(elf_header_)));
+ pieces.push_back(ElfFilePiece("Program headers", PHDR_OFFSET,
+ &program_headers_, sizeof(program_headers_)));
+ pieces.push_back(ElfFilePiece(".dynamic", dynamic_builder_.section_.sh_offset,
+ dynamic.data(), dynamic_builder_.section_.sh_size));
+ pieces.push_back(ElfFilePiece(".dynsym", dynsym_builder_.section_.sh_offset,
+ dynsym.data(), dynsym.size() * sizeof(Elf_Sym)));
+ pieces.push_back(ElfFilePiece(".dynstr", dynsym_builder_.GetStrTab()->section_.sh_offset,
+ dynstr_.c_str(), dynstr_.size()));
+ pieces.push_back(ElfFilePiece(".hash", hash_builder_.section_.sh_offset,
+ hash_.data(), hash_.size() * sizeof(Elf_Word)));
+ pieces.push_back(ElfFilePiece(".rodata", rodata_builder_.section_.sh_offset,
+ nullptr, rodata_builder_.section_.sh_size));
+ pieces.push_back(ElfFilePiece(".text", text_builder_.section_.sh_offset,
+ nullptr, text_builder_.section_.sh_size));
+ if (IncludingDebugSymbols()) {
+ pieces.push_back(ElfFilePiece(".symtab", symtab_builder_.section_.sh_offset,
+ symtab.data(), symtab.size() * sizeof(Elf_Sym)));
+ pieces.push_back(ElfFilePiece(".strtab", symtab_builder_.GetStrTab()->section_.sh_offset,
+ strtab.c_str(), strtab.size()));
+ }
+ pieces.push_back(ElfFilePiece(".shstrtab", shstrtab_builder_.section_.sh_offset,
+ &shstrtab_[0], shstrtab_.size()));
+ for (uint32_t i = 0; i < section_ptrs_.size(); ++i) {
+ // Just add all the sections in induvidually since they are all over the
+ // place on the heap/stack.
+ Elf_Word cur_off = sections_offset + i * sizeof(Elf_Shdr);
+ pieces.push_back(ElfFilePiece("section table piece", cur_off,
+ section_ptrs_[i], sizeof(Elf_Shdr)));
+ }
+
+ if (!WriteOutFile(pieces)) {
+ LOG(ERROR) << "Unable to write to file " << elf_file_->GetPath();
+ return false;
+ }
+ // write out the actual oat file data.
+ Elf_Word oat_data_offset = rodata_builder_.section_.sh_offset;
+ if (static_cast<off_t>(oat_data_offset) != lseek(elf_file_->Fd(), oat_data_offset, SEEK_SET)) {
+ PLOG(ERROR) << "Failed to seek to .rodata offset " << oat_data_offset
+ << " for " << elf_file_->GetPath();
+ return false;
+ }
+ std::unique_ptr<BufferedOutputStream> output_stream(
+ new BufferedOutputStream(new FileOutputStream(elf_file_)));
+ if (!oat_writer_->Write(output_stream.get())) {
+ PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file_->GetPath();
+ return false;
+ }
+
+ return true;
+ }
+
+ // Adds the given raw section to the builder. This will copy it. The caller
+ // is responsible for deallocating their copy.
+ void RegisterRawSection(ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> bld) {
+ other_builders_.push_back(bld);
+ }
+
+ private:
+ CodeOutput* oat_writer_;
+ File* elf_file_;
+ const bool add_symbols_;
+ const bool debug_logging_;
+
+ bool fatal_error_ = false;
+
+ // What phdr is.
+ static const uint32_t PHDR_OFFSET = sizeof(Elf_Ehdr);
+ enum : uint8_t {
+ PH_PHDR = 0,
+ PH_LOAD_R__ = 1,
+ PH_LOAD_R_X = 2,
+ PH_LOAD_RW_ = 3,
+ PH_DYNAMIC = 4,
+ PH_NUM = 5,
+ };
+ static const uint32_t PHDR_SIZE = sizeof(Elf_Phdr) * PH_NUM;
+ Elf_Phdr program_headers_[PH_NUM];
+
+ Elf_Ehdr elf_header_;
+
+ Elf_Shdr null_hdr_;
+ std::string shstrtab_;
+ uint32_t section_index_;
+ std::string dynstr_;
+ uint32_t dynstr_soname_offset_;
+ std::vector<Elf_Shdr*> section_ptrs_;
+ std::vector<Elf_Word> hash_;
+
+ public:
+ ElfOatSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> text_builder_;
+ ElfOatSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> rodata_builder_;
+ ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Sym, Elf_Shdr> dynsym_builder_;
+ ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Sym, Elf_Shdr> symtab_builder_;
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> hash_builder_;
+ ElfDynamicBuilder<Elf_Word, Elf_Sword, Elf_Dyn, Elf_Shdr> dynamic_builder_;
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> shstrtab_builder_;
+ std::vector<ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>> other_builders_;
+
+ private:
+ void SetISA(InstructionSet isa) {
+ switch (isa) {
+ case kArm:
+ // Fall through.
+ case kThumb2: {
+ elf_header_.e_machine = EM_ARM;
+ elf_header_.e_flags = EF_ARM_EABI_VER5;
+ break;
+ }
+ case kArm64: {
+ elf_header_.e_machine = EM_AARCH64;
+ elf_header_.e_flags = 0;
+ break;
+ }
+ case kX86: {
+ elf_header_.e_machine = EM_386;
+ elf_header_.e_flags = 0;
+ break;
+ }
+ case kX86_64: {
+ elf_header_.e_machine = EM_X86_64;
+ elf_header_.e_flags = 0;
+ break;
+ }
+ case kMips: {
+ elf_header_.e_machine = EM_MIPS;
+ elf_header_.e_flags = (EF_MIPS_NOREORDER |
+ EF_MIPS_PIC |
+ EF_MIPS_CPIC |
+ EF_MIPS_ABI_O32 |
+ EF_MIPS_ARCH_32R2);
+ break;
+ }
+ default: {
+ fatal_error_ = true;
+ LOG(FATAL) << "Unknown instruction set: " << isa;
+ break;
+ }
+ }
+ }
+
+ void SetupEhdr() {
+ memset(&elf_header_, 0, sizeof(elf_header_));
+ elf_header_.e_ident[EI_MAG0] = ELFMAG0;
+ elf_header_.e_ident[EI_MAG1] = ELFMAG1;
+ elf_header_.e_ident[EI_MAG2] = ELFMAG2;
+ elf_header_.e_ident[EI_MAG3] = ELFMAG3;
+ elf_header_.e_ident[EI_CLASS] = ELFCLASS32;
+ elf_header_.e_ident[EI_DATA] = ELFDATA2LSB;
+ elf_header_.e_ident[EI_VERSION] = EV_CURRENT;
+ elf_header_.e_ident[EI_OSABI] = ELFOSABI_LINUX;
+ elf_header_.e_ident[EI_ABIVERSION] = 0;
+ elf_header_.e_type = ET_DYN;
+ elf_header_.e_version = 1;
+ elf_header_.e_entry = 0;
+ elf_header_.e_ehsize = sizeof(Elf_Ehdr);
+ elf_header_.e_phentsize = sizeof(Elf_Phdr);
+ elf_header_.e_shentsize = sizeof(Elf_Shdr);
+ elf_header_.e_phoff = sizeof(Elf_Ehdr);
+ }
+
+ // Sets up a bunch of the required Dynamic Section entries.
+ // Namely it will initialize all the mandatory ones that it can.
+ // Specifically:
+ // DT_HASH
+ // DT_STRTAB
+ // DT_SYMTAB
+ // DT_SYMENT
+ //
+ // Some such as DT_SONAME, DT_STRSZ and DT_NULL will be put in later.
+ void SetupDynamic() {
+ dynamic_builder_.AddDynamicTag(DT_HASH, 0, &hash_builder_);
+ dynamic_builder_.AddDynamicTag(DT_STRTAB, 0, dynsym_builder_.GetStrTab());
+ dynamic_builder_.AddDynamicTag(DT_SYMTAB, 0, &dynsym_builder_);
+ dynamic_builder_.AddDynamicTag(DT_SYMENT, sizeof(Elf_Sym));
+ }
+
+ // Sets up the basic dynamic symbols that are needed, namely all those we
+ // can know already.
+ //
+ // Specifically adds:
+ // oatdata
+ // oatexec
+ // oatlastword
+ void SetupRequiredSymbols() {
+ dynsym_builder_.AddSymbol("oatdata", &rodata_builder_, 0, true,
+ rodata_builder_.GetSize(), STB_GLOBAL, STT_OBJECT);
+ dynsym_builder_.AddSymbol("oatexec", &text_builder_, 0, true,
+ text_builder_.GetSize(), STB_GLOBAL, STT_OBJECT);
+ dynsym_builder_.AddSymbol("oatlastword", &text_builder_, text_builder_.GetSize() - 4,
+ true, 4, STB_GLOBAL, STT_OBJECT);
+ }
+
+ void AssignSectionStr(ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *builder,
+ std::string* strtab) {
+ builder->section_.sh_name = strtab->size();
+ *strtab += builder->name_;
+ *strtab += '\0';
+ if (debug_logging_) {
+ LOG(INFO) << "adding section name \"" << builder->name_ << "\" "
+ << "to shstrtab at offset " << builder->section_.sh_name;
+ }
+ }
+
+ struct ElfFilePiece {
+ ElfFilePiece(const std::string& name, Elf_Word offset, const void* data, Elf_Word size)
+ : dbg_name_(name), offset_(offset), data_(data), size_(size) {}
+ ~ElfFilePiece() {}
+
+ const std::string& dbg_name_;
+ Elf_Word offset_;
+ const void *data_;
+ Elf_Word size_;
+ static bool Compare(ElfFilePiece a, ElfFilePiece b) {
+ return a.offset_ < b.offset_;
+ }
+ };
+
+ // Write each of the pieces out to the file.
+ bool WriteOutFile(const std::vector<ElfFilePiece>& pieces) {
+ // TODO It would be nice if this checked for overlap.
+ for (auto it = pieces.begin(); it != pieces.end(); ++it) {
+ if (it->data_) {
+ if (static_cast<off_t>(it->offset_) != lseek(elf_file_->Fd(), it->offset_, SEEK_SET)) {
+ PLOG(ERROR) << "Failed to seek to " << it->dbg_name_ << " offset location "
+ << it->offset_ << " for " << elf_file_->GetPath();
+ return false;
+ }
+ if (!elf_file_->WriteFully(it->data_, it->size_)) {
+ PLOG(ERROR) << "Failed to write " << it->dbg_name_ << " for " << elf_file_->GetPath();
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ bool IncludingDebugSymbols() { return add_symbols_ && symtab_builder_.GetSize() > 1; }
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_ELF_BUILDER_H_
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 4c69fc8..dbd3a37 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -23,6 +23,7 @@
#include "buffered_output_stream.h"
#include "driver/compiler_driver.h"
#include "dwarf.h"
+#include "elf_builder.h"
#include "elf_file.h"
#include "elf_utils.h"
#include "file_output_stream.h"
@@ -34,15 +35,6 @@
namespace art {
-template <typename Elf_Word, typename Elf_Shdr>
-static constexpr Elf_Word NextOffset(const Elf_Shdr& cur, const Elf_Shdr& prev) {
- return RoundUp(prev.sh_size + prev.sh_offset, cur.sh_addralign);
-}
-
-static uint8_t MakeStInfo(uint8_t binding, uint8_t type) {
- return ((binding) << 4) + ((type) & 0xf);
-}
-
static void PushByte(std::vector<uint8_t>* buf, int data) {
buf->push_back(data & 0xff);
}
@@ -83,826 +75,6 @@
typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
typename Elf_Phdr, typename Elf_Shdr>
bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::Init() {
- // The basic layout of the elf file. Order may be different in final output.
- // +-------------------------+
- // | Elf_Ehdr |
- // +-------------------------+
- // | Elf_Phdr PHDR |
- // | Elf_Phdr LOAD R | .dynsym .dynstr .hash .rodata
- // | Elf_Phdr LOAD R X | .text
- // | Elf_Phdr LOAD RW | .dynamic
- // | Elf_Phdr DYNAMIC | .dynamic
- // +-------------------------+
- // | .dynsym |
- // | Elf_Sym STN_UNDEF |
- // | Elf_Sym oatdata |
- // | Elf_Sym oatexec |
- // | Elf_Sym oatlastword |
- // +-------------------------+
- // | .dynstr |
- // | \0 |
- // | oatdata\0 |
- // | oatexec\0 |
- // | oatlastword\0 |
- // | boot.oat\0 |
- // +-------------------------+
- // | .hash |
- // | Elf_Word nbucket = b |
- // | Elf_Word nchain = c |
- // | Elf_Word bucket[0] |
- // | ... |
- // | Elf_Word bucket[b - 1] |
- // | Elf_Word chain[0] |
- // | ... |
- // | Elf_Word chain[c - 1] |
- // +-------------------------+
- // | .rodata |
- // | oatdata..oatexec-4 |
- // +-------------------------+
- // | .text |
- // | oatexec..oatlastword |
- // +-------------------------+
- // | .dynamic |
- // | Elf_Dyn DT_SONAME |
- // | Elf_Dyn DT_HASH |
- // | Elf_Dyn DT_SYMTAB |
- // | Elf_Dyn DT_SYMENT |
- // | Elf_Dyn DT_STRTAB |
- // | Elf_Dyn DT_STRSZ |
- // | Elf_Dyn DT_NULL |
- // +-------------------------+ (Optional)
- // | .strtab | (Optional)
- // | program symbol names | (Optional)
- // +-------------------------+ (Optional)
- // | .symtab | (Optional)
- // | program symbols | (Optional)
- // +-------------------------+
- // | .shstrtab |
- // | \0 |
- // | .dynamic\0 |
- // | .dynsym\0 |
- // | .dynstr\0 |
- // | .hash\0 |
- // | .rodata\0 |
- // | .text\0 |
- // | .shstrtab\0 |
- // | .symtab\0 | (Optional)
- // | .strtab\0 | (Optional)
- // | .debug_str\0 | (Optional)
- // | .debug_info\0 | (Optional)
- // | .eh_frame\0 | (Optional)
- // | .debug_line\0 | (Optional)
- // | .debug_abbrev\0 | (Optional)
- // +-------------------------+ (Optional)
- // | .debug_info | (Optional)
- // +-------------------------+ (Optional)
- // | .debug_abbrev | (Optional)
- // +-------------------------+ (Optional)
- // | .eh_frame | (Optional)
- // +-------------------------+ (Optional)
- // | .debug_line | (Optional)
- // +-------------------------+ (Optional)
- // | .debug_str | (Optional)
- // +-------------------------+ (Optional)
- // | Elf_Shdr NULL |
- // | Elf_Shdr .dynsym |
- // | Elf_Shdr .dynstr |
- // | Elf_Shdr .hash |
- // | Elf_Shdr .text |
- // | Elf_Shdr .rodata |
- // | Elf_Shdr .dynamic |
- // | Elf_Shdr .shstrtab |
- // | Elf_Shdr .debug_info | (Optional)
- // | Elf_Shdr .debug_abbrev | (Optional)
- // | Elf_Shdr .eh_frame | (Optional)
- // | Elf_Shdr .debug_line | (Optional)
- // | Elf_Shdr .debug_str | (Optional)
- // +-------------------------+
-
- if (fatal_error_) {
- return false;
- }
- // Step 1. Figure out all the offsets.
-
- if (debug_logging_) {
- LOG(INFO) << "phdr_offset=" << PHDR_OFFSET << std::hex << " " << PHDR_OFFSET;
- LOG(INFO) << "phdr_size=" << PHDR_SIZE << std::hex << " " << PHDR_SIZE;
- }
-
- memset(&program_headers_, 0, sizeof(program_headers_));
- program_headers_[PH_PHDR].p_type = PT_PHDR;
- program_headers_[PH_PHDR].p_offset = PHDR_OFFSET;
- program_headers_[PH_PHDR].p_vaddr = PHDR_OFFSET;
- program_headers_[PH_PHDR].p_paddr = PHDR_OFFSET;
- program_headers_[PH_PHDR].p_filesz = sizeof(program_headers_);
- program_headers_[PH_PHDR].p_memsz = sizeof(program_headers_);
- program_headers_[PH_PHDR].p_flags = PF_R;
- program_headers_[PH_PHDR].p_align = sizeof(Elf_Word);
-
- program_headers_[PH_LOAD_R__].p_type = PT_LOAD;
- program_headers_[PH_LOAD_R__].p_offset = 0;
- program_headers_[PH_LOAD_R__].p_vaddr = 0;
- program_headers_[PH_LOAD_R__].p_paddr = 0;
- program_headers_[PH_LOAD_R__].p_flags = PF_R;
-
- program_headers_[PH_LOAD_R_X].p_type = PT_LOAD;
- program_headers_[PH_LOAD_R_X].p_flags = PF_R | PF_X;
-
- program_headers_[PH_LOAD_RW_].p_type = PT_LOAD;
- program_headers_[PH_LOAD_RW_].p_flags = PF_R | PF_W;
-
- program_headers_[PH_DYNAMIC].p_type = PT_DYNAMIC;
- program_headers_[PH_DYNAMIC].p_flags = PF_R | PF_W;
-
- // Get the dynstr string.
- dynstr_ = dynsym_builder_.GenerateStrtab();
-
- // Add the SONAME to the dynstr.
- dynstr_soname_offset_ = dynstr_.size();
- std::string file_name(elf_file_->GetPath());
- size_t directory_separator_pos = file_name.rfind('/');
- if (directory_separator_pos != std::string::npos) {
- file_name = file_name.substr(directory_separator_pos + 1);
- }
- dynstr_ += file_name;
- dynstr_ += '\0';
- if (debug_logging_) {
- LOG(INFO) << "dynstr size (bytes) =" << dynstr_.size()
- << std::hex << " " << dynstr_.size();
- LOG(INFO) << "dynsym size (elements)=" << dynsym_builder_.GetSize()
- << std::hex << " " << dynsym_builder_.GetSize();
- }
-
- // Get the section header string table.
- shstrtab_ += '\0';
-
- // Setup sym_undef
- memset(&null_hdr_, 0, sizeof(null_hdr_));
- null_hdr_.sh_type = SHT_NULL;
- null_hdr_.sh_link = SHN_UNDEF;
- section_ptrs_.push_back(&null_hdr_);
-
- section_index_ = 1;
-
- // setup .dynsym
- section_ptrs_.push_back(&dynsym_builder_.section_);
- AssignSectionStr(&dynsym_builder_, &shstrtab_);
- dynsym_builder_.section_index_ = section_index_++;
-
- // Setup .dynstr
- section_ptrs_.push_back(&dynsym_builder_.strtab_.section_);
- AssignSectionStr(&dynsym_builder_.strtab_, &shstrtab_);
- dynsym_builder_.strtab_.section_index_ = section_index_++;
-
- // Setup .hash
- section_ptrs_.push_back(&hash_builder_.section_);
- AssignSectionStr(&hash_builder_, &shstrtab_);
- hash_builder_.section_index_ = section_index_++;
-
- // Setup .rodata
- section_ptrs_.push_back(&rodata_builder_.section_);
- AssignSectionStr(&rodata_builder_, &shstrtab_);
- rodata_builder_.section_index_ = section_index_++;
-
- // Setup .text
- section_ptrs_.push_back(&text_builder_.section_);
- AssignSectionStr(&text_builder_, &shstrtab_);
- text_builder_.section_index_ = section_index_++;
-
- // Setup .dynamic
- section_ptrs_.push_back(&dynamic_builder_.section_);
- AssignSectionStr(&dynamic_builder_, &shstrtab_);
- dynamic_builder_.section_index_ = section_index_++;
-
- // Fill in the hash section.
- hash_ = dynsym_builder_.GenerateHashContents();
-
- if (debug_logging_) {
- LOG(INFO) << ".hash size (bytes)=" << hash_.size() * sizeof(Elf_Word)
- << std::hex << " " << hash_.size() * sizeof(Elf_Word);
- }
-
- Elf_Word base_offset = sizeof(Elf_Ehdr) + sizeof(program_headers_);
-
- // Get the layout in the sections.
- //
- // Get the layout of the dynsym section.
- dynsym_builder_.section_.sh_offset = RoundUp(base_offset, dynsym_builder_.section_.sh_addralign);
- dynsym_builder_.section_.sh_addr = dynsym_builder_.section_.sh_offset;
- dynsym_builder_.section_.sh_size = dynsym_builder_.GetSize() * sizeof(Elf_Sym);
- dynsym_builder_.section_.sh_link = dynsym_builder_.GetLink();
-
- // Get the layout of the dynstr section.
- dynsym_builder_.strtab_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (dynsym_builder_.strtab_.section_,
- dynsym_builder_.section_);
- dynsym_builder_.strtab_.section_.sh_addr = dynsym_builder_.strtab_.section_.sh_offset;
- dynsym_builder_.strtab_.section_.sh_size = dynstr_.size();
- dynsym_builder_.strtab_.section_.sh_link = dynsym_builder_.strtab_.GetLink();
-
- // Get the layout of the hash section
- hash_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (hash_builder_.section_,
- dynsym_builder_.strtab_.section_);
- hash_builder_.section_.sh_addr = hash_builder_.section_.sh_offset;
- hash_builder_.section_.sh_size = hash_.size() * sizeof(Elf_Word);
- hash_builder_.section_.sh_link = hash_builder_.GetLink();
-
- // Get the layout of the rodata section.
- rodata_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (rodata_builder_.section_,
- hash_builder_.section_);
- rodata_builder_.section_.sh_addr = rodata_builder_.section_.sh_offset;
- rodata_builder_.section_.sh_size = rodata_builder_.size_;
- rodata_builder_.section_.sh_link = rodata_builder_.GetLink();
-
- // Get the layout of the text section.
- text_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (text_builder_.section_, rodata_builder_.section_);
- text_builder_.section_.sh_addr = text_builder_.section_.sh_offset;
- text_builder_.section_.sh_size = text_builder_.size_;
- text_builder_.section_.sh_link = text_builder_.GetLink();
- CHECK_ALIGNED(rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size, kPageSize);
-
- // Get the layout of the dynamic section.
- dynamic_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (dynamic_builder_.section_,
- text_builder_.section_);
- dynamic_builder_.section_.sh_addr = dynamic_builder_.section_.sh_offset;
- dynamic_builder_.section_.sh_size = dynamic_builder_.GetSize() * sizeof(Elf_Dyn);
- dynamic_builder_.section_.sh_link = dynamic_builder_.GetLink();
-
- if (debug_logging_) {
- LOG(INFO) << "dynsym off=" << dynsym_builder_.section_.sh_offset
- << " dynsym size=" << dynsym_builder_.section_.sh_size;
- LOG(INFO) << "dynstr off=" << dynsym_builder_.strtab_.section_.sh_offset
- << " dynstr size=" << dynsym_builder_.strtab_.section_.sh_size;
- LOG(INFO) << "hash off=" << hash_builder_.section_.sh_offset
- << " hash size=" << hash_builder_.section_.sh_size;
- LOG(INFO) << "rodata off=" << rodata_builder_.section_.sh_offset
- << " rodata size=" << rodata_builder_.section_.sh_size;
- LOG(INFO) << "text off=" << text_builder_.section_.sh_offset
- << " text size=" << text_builder_.section_.sh_size;
- LOG(INFO) << "dynamic off=" << dynamic_builder_.section_.sh_offset
- << " dynamic size=" << dynamic_builder_.section_.sh_size;
- }
-
- return true;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::Write() {
- std::vector<ElfFilePiece> pieces;
- Elf_Shdr prev = dynamic_builder_.section_;
- std::string strtab;
-
- if (IncludingDebugSymbols()) {
- // Setup .symtab
- section_ptrs_.push_back(&symtab_builder_.section_);
- AssignSectionStr(&symtab_builder_, &shstrtab_);
- symtab_builder_.section_index_ = section_index_++;
-
- // Setup .strtab
- section_ptrs_.push_back(&symtab_builder_.strtab_.section_);
- AssignSectionStr(&symtab_builder_.strtab_, &shstrtab_);
- symtab_builder_.strtab_.section_index_ = section_index_++;
-
- strtab = symtab_builder_.GenerateStrtab();
- if (debug_logging_) {
- LOG(INFO) << "strtab size (bytes) =" << strtab.size()
- << std::hex << " " << strtab.size();
- LOG(INFO) << "symtab size (elements) =" << symtab_builder_.GetSize()
- << std::hex << " " << symtab_builder_.GetSize();
- }
- }
-
- // Setup all the other sections.
- for (ElfRawSectionBuilder *builder = other_builders_.data(),
- *end = builder + other_builders_.size();
- builder != end; ++builder) {
- section_ptrs_.push_back(&builder->section_);
- AssignSectionStr(builder, &shstrtab_);
- builder->section_index_ = section_index_++;
- }
-
- // Setup shstrtab
- section_ptrs_.push_back(&shstrtab_builder_.section_);
- AssignSectionStr(&shstrtab_builder_, &shstrtab_);
- shstrtab_builder_.section_index_ = section_index_++;
-
- if (debug_logging_) {
- LOG(INFO) << ".shstrtab size (bytes) =" << shstrtab_.size()
- << std::hex << " " << shstrtab_.size();
- LOG(INFO) << "section list size (elements)=" << section_ptrs_.size()
- << std::hex << " " << section_ptrs_.size();
- }
-
- if (IncludingDebugSymbols()) {
- // Get the layout of the symtab section.
- symtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (symtab_builder_.section_,
- dynamic_builder_.section_);
- symtab_builder_.section_.sh_addr = 0;
- // Add to leave space for the null symbol.
- symtab_builder_.section_.sh_size = symtab_builder_.GetSize() * sizeof(Elf_Sym);
- symtab_builder_.section_.sh_link = symtab_builder_.GetLink();
-
- // Get the layout of the dynstr section.
- symtab_builder_.strtab_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (symtab_builder_.strtab_.section_,
- symtab_builder_.section_);
- symtab_builder_.strtab_.section_.sh_addr = 0;
- symtab_builder_.strtab_.section_.sh_size = strtab.size();
- symtab_builder_.strtab_.section_.sh_link = symtab_builder_.strtab_.GetLink();
-
- prev = symtab_builder_.strtab_.section_;
- if (debug_logging_) {
- LOG(INFO) << "symtab off=" << symtab_builder_.section_.sh_offset
- << " symtab size=" << symtab_builder_.section_.sh_size;
- LOG(INFO) << "strtab off=" << symtab_builder_.strtab_.section_.sh_offset
- << " strtab size=" << symtab_builder_.strtab_.section_.sh_size;
- }
- }
-
- // Get the layout of the extra sections. (This will deal with the debug
- // sections if they are there)
- for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) {
- it->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>(it->section_, prev);
- it->section_.sh_addr = 0;
- it->section_.sh_size = it->GetBuffer()->size();
- it->section_.sh_link = it->GetLink();
- pieces.push_back(ElfFilePiece(it->name_, it->section_.sh_offset,
- it->GetBuffer()->data(), it->GetBuffer()->size()));
- prev = it->section_;
- if (debug_logging_) {
- LOG(INFO) << it->name_ << " off=" << it->section_.sh_offset
- << " " << it->name_ << " size=" << it->section_.sh_size;
- }
- }
-
- // Get the layout of the shstrtab section
- shstrtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (shstrtab_builder_.section_, prev);
- shstrtab_builder_.section_.sh_addr = 0;
- shstrtab_builder_.section_.sh_size = shstrtab_.size();
- shstrtab_builder_.section_.sh_link = shstrtab_builder_.GetLink();
- if (debug_logging_) {
- LOG(INFO) << "shstrtab off=" << shstrtab_builder_.section_.sh_offset
- << " shstrtab size=" << shstrtab_builder_.section_.sh_size;
- }
-
- // The section list comes after come after.
- Elf_Word sections_offset = RoundUp(
- shstrtab_builder_.section_.sh_offset + shstrtab_builder_.section_.sh_size,
- sizeof(Elf_Word));
-
- // Setup the actual symbol arrays.
- std::vector<Elf_Sym> dynsym = dynsym_builder_.GenerateSymtab();
- CHECK_EQ(dynsym.size() * sizeof(Elf_Sym), dynsym_builder_.section_.sh_size);
- std::vector<Elf_Sym> symtab;
- if (IncludingDebugSymbols()) {
- symtab = symtab_builder_.GenerateSymtab();
- CHECK_EQ(symtab.size() * sizeof(Elf_Sym), symtab_builder_.section_.sh_size);
- }
-
- // Setup the dynamic section.
- // This will add the 2 values we cannot know until now time, namely the size
- // and the soname_offset.
- std::vector<Elf_Dyn> dynamic = dynamic_builder_.GetDynamics(dynstr_.size(),
- dynstr_soname_offset_);
- CHECK_EQ(dynamic.size() * sizeof(Elf_Dyn), dynamic_builder_.section_.sh_size);
-
- // Finish setup of the program headers now that we know the layout of the
- // whole file.
- Elf_Word load_r_size = rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size;
- program_headers_[PH_LOAD_R__].p_filesz = load_r_size;
- program_headers_[PH_LOAD_R__].p_memsz = load_r_size;
- program_headers_[PH_LOAD_R__].p_align = rodata_builder_.section_.sh_addralign;
-
- Elf_Word load_rx_size = text_builder_.section_.sh_size;
- program_headers_[PH_LOAD_R_X].p_offset = text_builder_.section_.sh_offset;
- program_headers_[PH_LOAD_R_X].p_vaddr = text_builder_.section_.sh_offset;
- program_headers_[PH_LOAD_R_X].p_paddr = text_builder_.section_.sh_offset;
- program_headers_[PH_LOAD_R_X].p_filesz = load_rx_size;
- program_headers_[PH_LOAD_R_X].p_memsz = load_rx_size;
- program_headers_[PH_LOAD_R_X].p_align = text_builder_.section_.sh_addralign;
-
- program_headers_[PH_LOAD_RW_].p_offset = dynamic_builder_.section_.sh_offset;
- program_headers_[PH_LOAD_RW_].p_vaddr = dynamic_builder_.section_.sh_offset;
- program_headers_[PH_LOAD_RW_].p_paddr = dynamic_builder_.section_.sh_offset;
- program_headers_[PH_LOAD_RW_].p_filesz = dynamic_builder_.section_.sh_size;
- program_headers_[PH_LOAD_RW_].p_memsz = dynamic_builder_.section_.sh_size;
- program_headers_[PH_LOAD_RW_].p_align = dynamic_builder_.section_.sh_addralign;
-
- program_headers_[PH_DYNAMIC].p_offset = dynamic_builder_.section_.sh_offset;
- program_headers_[PH_DYNAMIC].p_vaddr = dynamic_builder_.section_.sh_offset;
- program_headers_[PH_DYNAMIC].p_paddr = dynamic_builder_.section_.sh_offset;
- program_headers_[PH_DYNAMIC].p_filesz = dynamic_builder_.section_.sh_size;
- program_headers_[PH_DYNAMIC].p_memsz = dynamic_builder_.section_.sh_size;
- program_headers_[PH_DYNAMIC].p_align = dynamic_builder_.section_.sh_addralign;
-
- // Finish setup of the Ehdr values.
- elf_header_.e_phoff = PHDR_OFFSET;
- elf_header_.e_shoff = sections_offset;
- elf_header_.e_phnum = PH_NUM;
- elf_header_.e_shnum = section_ptrs_.size();
- elf_header_.e_shstrndx = shstrtab_builder_.section_index_;
-
- // Add the rest of the pieces to the list.
- pieces.push_back(ElfFilePiece("Elf Header", 0, &elf_header_, sizeof(elf_header_)));
- pieces.push_back(ElfFilePiece("Program headers", PHDR_OFFSET,
- &program_headers_, sizeof(program_headers_)));
- pieces.push_back(ElfFilePiece(".dynamic", dynamic_builder_.section_.sh_offset,
- dynamic.data(), dynamic_builder_.section_.sh_size));
- pieces.push_back(ElfFilePiece(".dynsym", dynsym_builder_.section_.sh_offset,
- dynsym.data(), dynsym.size() * sizeof(Elf_Sym)));
- pieces.push_back(ElfFilePiece(".dynstr", dynsym_builder_.strtab_.section_.sh_offset,
- dynstr_.c_str(), dynstr_.size()));
- pieces.push_back(ElfFilePiece(".hash", hash_builder_.section_.sh_offset,
- hash_.data(), hash_.size() * sizeof(Elf_Word)));
- pieces.push_back(ElfFilePiece(".rodata", rodata_builder_.section_.sh_offset,
- nullptr, rodata_builder_.section_.sh_size));
- pieces.push_back(ElfFilePiece(".text", text_builder_.section_.sh_offset,
- nullptr, text_builder_.section_.sh_size));
- if (IncludingDebugSymbols()) {
- pieces.push_back(ElfFilePiece(".symtab", symtab_builder_.section_.sh_offset,
- symtab.data(), symtab.size() * sizeof(Elf_Sym)));
- pieces.push_back(ElfFilePiece(".strtab", symtab_builder_.strtab_.section_.sh_offset,
- strtab.c_str(), strtab.size()));
- }
- pieces.push_back(ElfFilePiece(".shstrtab", shstrtab_builder_.section_.sh_offset,
- &shstrtab_[0], shstrtab_.size()));
- for (uint32_t i = 0; i < section_ptrs_.size(); ++i) {
- // Just add all the sections in induvidually since they are all over the
- // place on the heap/stack.
- Elf_Word cur_off = sections_offset + i * sizeof(Elf_Shdr);
- pieces.push_back(ElfFilePiece("section table piece", cur_off,
- section_ptrs_[i], sizeof(Elf_Shdr)));
- }
-
- if (!WriteOutFile(pieces)) {
- LOG(ERROR) << "Unable to write to file " << elf_file_->GetPath();
- return false;
- }
- // write out the actual oat file data.
- Elf_Word oat_data_offset = rodata_builder_.section_.sh_offset;
- if (static_cast<off_t>(oat_data_offset) != lseek(elf_file_->Fd(), oat_data_offset, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to .rodata offset " << oat_data_offset
- << " for " << elf_file_->GetPath();
- return false;
- }
- std::unique_ptr<BufferedOutputStream> output_stream(
- new BufferedOutputStream(new FileOutputStream(elf_file_)));
- if (!oat_writer_->Write(output_stream.get())) {
- PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file_->GetPath();
- return false;
- }
-
- return true;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::WriteOutFile(const std::vector<ElfFilePiece>& pieces) {
- // TODO It would be nice if this checked for overlap.
- for (auto it = pieces.begin(); it != pieces.end(); ++it) {
- if (it->data_) {
- if (static_cast<off_t>(it->offset_) != lseek(elf_file_->Fd(), it->offset_, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to " << it->dbg_name_ << " offset location "
- << it->offset_ << " for " << elf_file_->GetPath();
- return false;
- }
- if (!elf_file_->WriteFully(it->data_, it->size_)) {
- PLOG(ERROR) << "Failed to write " << it->dbg_name_ << " for " << elf_file_->GetPath();
- return false;
- }
- }
- }
- return true;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::SetupDynamic() {
- dynamic_builder_.AddDynamicTag(DT_HASH, 0, &hash_builder_);
- dynamic_builder_.AddDynamicTag(DT_STRTAB, 0, &dynsym_builder_.strtab_);
- dynamic_builder_.AddDynamicTag(DT_SYMTAB, 0, &dynsym_builder_);
- dynamic_builder_.AddDynamicTag(DT_SYMENT, sizeof(Elf_Sym));
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::SetupRequiredSymbols() {
- dynsym_builder_.AddSymbol("oatdata", &rodata_builder_, 0, true,
- rodata_builder_.size_, STB_GLOBAL, STT_OBJECT);
- dynsym_builder_.AddSymbol("oatexec", &text_builder_, 0, true,
- text_builder_.size_, STB_GLOBAL, STT_OBJECT);
- dynsym_builder_.AddSymbol("oatlastword", &text_builder_, text_builder_.size_ - 4,
- true, 4, STB_GLOBAL, STT_OBJECT);
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfDynamicBuilder::AddDynamicTag(Elf_Sword tag, Elf_Word d_un) {
- if (tag == DT_NULL) {
- return;
- }
- dynamics_.push_back({nullptr, tag, d_un});
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfDynamicBuilder::AddDynamicTag(Elf_Sword tag, Elf_Word d_un,
- ElfSectionBuilder* section) {
- if (tag == DT_NULL) {
- return;
- }
- dynamics_.push_back({section, tag, d_un});
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-std::vector<Elf_Dyn> ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfDynamicBuilder::GetDynamics(Elf_Word strsz,
- Elf_Word soname) {
- std::vector<Elf_Dyn> ret;
- for (auto it = dynamics_.cbegin(); it != dynamics_.cend(); ++it) {
- if (it->section_) {
- // We are adding an address relative to a section.
- ret.push_back(
- {it->tag_, {it->off_ + it->section_->section_.sh_addr}});
- } else {
- ret.push_back({it->tag_, {it->off_}});
- }
- }
- ret.push_back({DT_STRSZ, {strsz}});
- ret.push_back({DT_SONAME, {soname}});
- ret.push_back({DT_NULL, {0}});
- return ret;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-std::vector<Elf_Sym> ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfSymtabBuilder::GenerateSymtab() {
- std::vector<Elf_Sym> ret;
- Elf_Sym undef_sym;
- memset(&undef_sym, 0, sizeof(undef_sym));
- undef_sym.st_shndx = SHN_UNDEF;
- ret.push_back(undef_sym);
-
- for (auto it = symbols_.cbegin(); it != symbols_.cend(); ++it) {
- Elf_Sym sym;
- memset(&sym, 0, sizeof(sym));
- sym.st_name = it->name_idx_;
- if (it->is_relative_) {
- sym.st_value = it->addr_ + it->section_->section_.sh_offset;
- } else {
- sym.st_value = it->addr_;
- }
- sym.st_size = it->size_;
- sym.st_other = it->other_;
- sym.st_shndx = it->section_->section_index_;
- sym.st_info = it->info_;
-
- ret.push_back(sym);
- }
- return ret;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-std::string ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfSymtabBuilder::GenerateStrtab() {
- std::string tab;
- tab += '\0';
- for (auto it = symbols_.begin(); it != symbols_.end(); ++it) {
- it->name_idx_ = tab.size();
- tab += it->name_;
- tab += '\0';
- }
- strtab_.section_.sh_size = tab.size();
- return tab;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::AssignSectionStr(
- ElfSectionBuilder* builder, std::string* strtab) {
- builder->section_.sh_name = strtab->size();
- *strtab += builder->name_;
- *strtab += '\0';
- if (debug_logging_) {
- LOG(INFO) << "adding section name \"" << builder->name_ << "\" "
- << "to shstrtab at offset " << builder->section_.sh_name;
- }
-}
-
-// from bionic
-static unsigned elfhash(const char *_name) {
- const unsigned char *name = (const unsigned char *) _name;
- unsigned h = 0, g;
-
- while (*name) {
- h = (h << 4) + *name++;
- g = h & 0xf0000000;
- h ^= g;
- h ^= g >> 24;
- }
- return h;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-std::vector<Elf_Word> ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfSymtabBuilder::GenerateHashContents() {
- // Here is how The ELF hash table works.
- // There are 3 arrays to worry about.
- // * The symbol table where the symbol information is.
- // * The bucket array which is an array of indexes into the symtab and chain.
- // * The chain array which is also an array of indexes into the symtab and chain.
- //
- // Lets say the state is something like this.
- // +--------+ +--------+ +-----------+
- // | symtab | | bucket | | chain |
- // | null | | 1 | | STN_UNDEF |
- // | <sym1> | | 4 | | 2 |
- // | <sym2> | | | | 5 |
- // | <sym3> | | | | STN_UNDEF |
- // | <sym4> | | | | 3 |
- // | <sym5> | | | | STN_UNDEF |
- // +--------+ +--------+ +-----------+
- //
- // The lookup process (in python psudocode) is
- //
- // def GetSym(name):
- // # NB STN_UNDEF == 0
- // indx = bucket[elfhash(name) % num_buckets]
- // while indx != STN_UNDEF:
- // if GetSymbolName(symtab[indx]) == name:
- // return symtab[indx]
- // indx = chain[indx]
- // return SYMBOL_NOT_FOUND
- //
- // Between bucket and chain arrays every symtab index must be present exactly
- // once (except for STN_UNDEF, which must be present 1 + num_bucket times).
-
- // Select number of buckets.
- // This is essentially arbitrary.
- Elf_Word nbuckets;
- Elf_Word chain_size = GetSize();
- if (symbols_.size() < 8) {
- nbuckets = 2;
- } else if (symbols_.size() < 32) {
- nbuckets = 4;
- } else if (symbols_.size() < 256) {
- nbuckets = 16;
- } else {
- // Have about 32 ids per bucket.
- nbuckets = RoundUp(symbols_.size()/32, 2);
- }
- std::vector<Elf_Word> hash;
- hash.push_back(nbuckets);
- hash.push_back(chain_size);
- uint32_t bucket_offset = hash.size();
- uint32_t chain_offset = bucket_offset + nbuckets;
- hash.resize(hash.size() + nbuckets + chain_size, 0);
-
- Elf_Word* buckets = hash.data() + bucket_offset;
- Elf_Word* chain = hash.data() + chain_offset;
-
- // Set up the actual hash table.
- for (Elf_Word i = 0; i < symbols_.size(); i++) {
- // Add 1 since we need to have the null symbol that is not in the symbols
- // list.
- Elf_Word index = i + 1;
- Elf_Word hash_val = static_cast<Elf_Word>(elfhash(symbols_[i].name_.c_str())) % nbuckets;
- if (buckets[hash_val] == 0) {
- buckets[hash_val] = index;
- } else {
- hash_val = buckets[hash_val];
- CHECK_LT(hash_val, chain_size);
- while (chain[hash_val] != 0) {
- hash_val = chain[hash_val];
- CHECK_LT(hash_val, chain_size);
- }
- chain[hash_val] = index;
- // Check for loops. Works because if this is non-empty then there must be
- // another cell which already contains the same symbol index as this one,
- // which means some symbol has more then one name, which isn't allowed.
- CHECK_EQ(chain[index], static_cast<Elf_Word>(0));
- }
- }
-
- return hash;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::SetupEhdr() {
- memset(&elf_header_, 0, sizeof(elf_header_));
- elf_header_.e_ident[EI_MAG0] = ELFMAG0;
- elf_header_.e_ident[EI_MAG1] = ELFMAG1;
- elf_header_.e_ident[EI_MAG2] = ELFMAG2;
- elf_header_.e_ident[EI_MAG3] = ELFMAG3;
- elf_header_.e_ident[EI_CLASS] = ELFCLASS32;
- elf_header_.e_ident[EI_DATA] = ELFDATA2LSB;
- elf_header_.e_ident[EI_VERSION] = EV_CURRENT;
- elf_header_.e_ident[EI_OSABI] = ELFOSABI_LINUX;
- elf_header_.e_ident[EI_ABIVERSION] = 0;
- elf_header_.e_type = ET_DYN;
- elf_header_.e_version = 1;
- elf_header_.e_entry = 0;
- elf_header_.e_ehsize = sizeof(Elf_Ehdr);
- elf_header_.e_phentsize = sizeof(Elf_Phdr);
- elf_header_.e_shentsize = sizeof(Elf_Shdr);
- elf_header_.e_phoff = sizeof(Elf_Ehdr);
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::SetISA(InstructionSet isa) {
- switch (isa) {
- case kArm:
- // Fall through.
- case kThumb2: {
- elf_header_.e_machine = EM_ARM;
- elf_header_.e_flags = EF_ARM_EABI_VER5;
- break;
- }
- case kArm64: {
- elf_header_.e_machine = EM_AARCH64;
- elf_header_.e_flags = 0;
- break;
- }
- case kX86: {
- elf_header_.e_machine = EM_386;
- elf_header_.e_flags = 0;
- break;
- }
- case kX86_64: {
- elf_header_.e_machine = EM_X86_64;
- elf_header_.e_flags = 0;
- break;
- }
- case kMips: {
- elf_header_.e_machine = EM_MIPS;
- elf_header_.e_flags = (EF_MIPS_NOREORDER |
- EF_MIPS_PIC |
- EF_MIPS_CPIC |
- EF_MIPS_ABI_O32 |
- EF_MIPS_ARCH_32R2);
- break;
- }
- default: {
- fatal_error_ = true;
- LOG(FATAL) << "Unknown instruction set: " << isa;
- break;
- }
- }
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfSymtabBuilder::AddSymbol(
- const std::string& name, const ElfSectionBuilder* section, Elf_Addr addr,
- bool is_relative, Elf_Word size, uint8_t binding, uint8_t type, uint8_t other) {
- CHECK(section);
- ElfSymtabBuilder::ElfSymbolState state {name, section, addr, size, is_relative,
- MakeStInfo(binding, type), other, 0};
- symbols_.push_back(state);
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::Create(File* elf_file,
OatWriter* oat_writer,
const std::vector<const DexFile*>& dex_files,
@@ -913,17 +85,14 @@
return elf_writer.Write(oat_writer, dex_files, android_root, is_host);
}
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
// Add patch information to this section. Each patch is a Elf_Word that
// identifies an offset from the start of the text section
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ReservePatchSpace(std::vector<uint8_t>* buffer, bool debug) {
+static void ReservePatchSpace(const CompilerDriver* compiler_driver, std::vector<uint8_t>* buffer,
+ bool debug) {
size_t size =
- compiler_driver_->GetCodeToPatch().size() +
- compiler_driver_->GetMethodsToPatch().size() +
- compiler_driver_->GetClassesToPatch().size();
+ compiler_driver->GetCodeToPatch().size() +
+ compiler_driver->GetMethodsToPatch().size() +
+ compiler_driver->GetClassesToPatch().size();
if (size == 0) {
if (debug) {
LOG(INFO) << "No patches to record";
@@ -1046,6 +215,25 @@
}
}
+class OatWriterWrapper : public CodeOutput {
+ public:
+ explicit OatWriterWrapper(OatWriter* oat_writer) : oat_writer_(oat_writer) {}
+
+ bool Write(OutputStream* out) OVERRIDE {
+ return oat_writer_->Write(out);
+ }
+ private:
+ OatWriter* oat_writer_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
+ typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
+ typename Elf_Phdr, typename Elf_Shdr>
+static void WriteDebugSymbols(const CompilerDriver* compiler_driver,
+ ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+ Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder,
+ OatWriter* oat_writer);
+
template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
typename Elf_Phdr, typename Elf_Shdr>
@@ -1059,145 +247,40 @@
Elf_Word oat_data_size = oat_header.GetExecutableOffset();
uint32_t oat_exec_size = oat_writer->GetSize() - oat_data_size;
- std::unique_ptr<ElfBuilder> builder(new ElfBuilder(
- oat_writer,
- elf_file_,
- compiler_driver_->GetInstructionSet(),
- 0,
- oat_data_size,
- oat_data_size,
- oat_exec_size,
- compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols(),
- debug));
+ OatWriterWrapper wrapper(oat_writer);
+
+ std::unique_ptr<ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+ Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr> > builder(
+ new ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+ Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>(
+ &wrapper,
+ elf_file_,
+ compiler_driver_->GetInstructionSet(),
+ 0,
+ oat_data_size,
+ oat_data_size,
+ oat_exec_size,
+ compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols(),
+ debug));
if (!builder->Init()) {
return false;
}
if (compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
- WriteDebugSymbols(builder.get(), oat_writer);
+ WriteDebugSymbols(compiler_driver_, builder.get(), oat_writer);
}
if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) {
- ElfRawSectionBuilder oat_patches(".oat_patches", SHT_OAT_PATCH, 0, NULL, 0,
- sizeof(uintptr_t), sizeof(uintptr_t));
- ReservePatchSpace(oat_patches.GetBuffer(), debug);
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> oat_patches(
+ ".oat_patches", SHT_OAT_PATCH, 0, NULL, 0, sizeof(uintptr_t), sizeof(uintptr_t));
+ ReservePatchSpace(compiler_driver_, oat_patches.GetBuffer(), debug);
builder->RegisterRawSection(oat_patches);
}
return builder->Write();
}
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::WriteDebugSymbols(ElfBuilder* builder, OatWriter* oat_writer) {
- std::unique_ptr<std::vector<uint8_t>> cfi_info(
- ConstructCIEFrame(compiler_driver_->GetInstructionSet()));
-
- Elf_Addr text_section_address = builder->text_builder_.section_.sh_addr;
-
- // Iterate over the compiled methods.
- const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetCFIMethodInfo();
- ElfSymtabBuilder* symtab = &builder->symtab_builder_;
- for (auto it = method_info.begin(); it != method_info.end(); ++it) {
- symtab->AddSymbol(it->method_name_, &builder->text_builder_, it->low_pc_, true,
- it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC);
-
- // Include CFI for compiled method, if possible.
- if (cfi_info.get() != nullptr) {
- DCHECK(it->compiled_method_ != nullptr);
-
- // Copy in the FDE, if present
- const std::vector<uint8_t>* fde = it->compiled_method_->GetCFIInfo();
- if (fde != nullptr) {
- // Copy the information into cfi_info and then fix the address in the new copy.
- int cur_offset = cfi_info->size();
- cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
-
- bool is_64bit = *(reinterpret_cast<const uint32_t*>(fde->data())) == 0xffffffff;
-
- // Set the 'CIE_pointer' field.
- uint64_t CIE_pointer = cur_offset + (is_64bit ? 12 : 4);
- uint64_t offset_to_update = CIE_pointer;
- if (is_64bit) {
- (*cfi_info)[offset_to_update+0] = CIE_pointer;
- (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
- (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
- (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
- (*cfi_info)[offset_to_update+4] = CIE_pointer >> 32;
- (*cfi_info)[offset_to_update+5] = CIE_pointer >> 40;
- (*cfi_info)[offset_to_update+6] = CIE_pointer >> 48;
- (*cfi_info)[offset_to_update+7] = CIE_pointer >> 56;
- } else {
- (*cfi_info)[offset_to_update+0] = CIE_pointer;
- (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
- (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
- (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
- }
-
- // Set the 'initial_location' field.
- offset_to_update += is_64bit ? 8 : 4;
- if (is_64bit) {
- const uint64_t quick_code_start = it->low_pc_ + text_section_address;
- (*cfi_info)[offset_to_update+0] = quick_code_start;
- (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
- (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
- (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
- (*cfi_info)[offset_to_update+4] = quick_code_start >> 32;
- (*cfi_info)[offset_to_update+5] = quick_code_start >> 40;
- (*cfi_info)[offset_to_update+6] = quick_code_start >> 48;
- (*cfi_info)[offset_to_update+7] = quick_code_start >> 56;
- } else {
- const uint32_t quick_code_start = it->low_pc_ + text_section_address;
- (*cfi_info)[offset_to_update+0] = quick_code_start;
- (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
- (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
- (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
- }
- }
- }
- }
-
- bool hasCFI = (cfi_info.get() != nullptr);
- bool hasLineInfo = false;
- for (auto& dbg_info : oat_writer->GetCFIMethodInfo()) {
- if (dbg_info.dbgstream_ != nullptr &&
- !dbg_info.compiled_method_->GetSrcMappingTable().empty()) {
- hasLineInfo = true;
- break;
- }
- }
-
- if (hasLineInfo || hasCFI) {
- ElfRawSectionBuilder debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
- ElfRawSectionBuilder debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
- ElfRawSectionBuilder debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
- ElfRawSectionBuilder debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-
- FillInCFIInformation(oat_writer, debug_info.GetBuffer(),
- debug_abbrev.GetBuffer(), debug_str.GetBuffer(),
- hasLineInfo ? debug_line.GetBuffer() : nullptr,
- text_section_address);
-
- builder->RegisterRawSection(debug_info);
- builder->RegisterRawSection(debug_abbrev);
-
- if (hasCFI) {
- ElfRawSectionBuilder eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0);
- eh_frame.SetBuffer(std::move(*cfi_info.get()));
- builder->RegisterRawSection(eh_frame);
- }
-
- if (hasLineInfo) {
- builder->RegisterRawSection(debug_line);
- }
-
- builder->RegisterRawSection(debug_str);
- }
-}
-
class LineTableGenerator FINAL : public Leb128Encoder {
public:
LineTableGenerator(int line_base, int line_range, int opcode_base,
@@ -1353,16 +436,19 @@
}
}
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
- typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
- typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::FillInCFIInformation(OatWriter* oat_writer,
- std::vector<uint8_t>* dbg_info,
- std::vector<uint8_t>* dbg_abbrev,
- std::vector<uint8_t>* dbg_str,
- std::vector<uint8_t>* dbg_line,
- uint32_t text_section_offset) {
+/*
+ * @brief Generate the DWARF debug_info and debug_abbrev sections
+ * @param oat_writer The Oat file Writer.
+ * @param dbg_info Compilation unit information.
+ * @param dbg_abbrev Abbreviations used to generate dbg_info.
+ * @param dbg_str Debug strings.
+ */
+static void FillInCFIInformation(OatWriter* oat_writer,
+ std::vector<uint8_t>* dbg_info,
+ std::vector<uint8_t>* dbg_abbrev,
+ std::vector<uint8_t>* dbg_str,
+ std::vector<uint8_t>* dbg_line,
+ uint32_t text_section_offset) {
const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetCFIMethodInfo();
uint32_t producer_str_offset = PushStr(dbg_str, "Android dex2oat");
@@ -1591,6 +677,130 @@
UpdateWord(dbg_info, cunit_length, dbg_info->size() - cunit_length - 4);
}
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
+ typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
+ typename Elf_Phdr, typename Elf_Shdr>
+static void WriteDebugSymbols(const CompilerDriver* compiler_driver,
+ ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+ Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder,
+ OatWriter* oat_writer) {
+ std::unique_ptr<std::vector<uint8_t>> cfi_info(
+ ConstructCIEFrame(compiler_driver->GetInstructionSet()));
+
+ Elf_Addr text_section_address = builder->text_builder_.section_.sh_addr;
+
+ // Iterate over the compiled methods.
+ const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetCFIMethodInfo();
+ ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr,
+ Elf_Sym, Elf_Shdr>* symtab = &builder->symtab_builder_;
+ for (auto it = method_info.begin(); it != method_info.end(); ++it) {
+ symtab->AddSymbol(it->method_name_, &builder->text_builder_, it->low_pc_, true,
+ it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC);
+
+ // Include CFI for compiled method, if possible.
+ if (cfi_info.get() != nullptr) {
+ DCHECK(it->compiled_method_ != nullptr);
+
+ // Copy in the FDE, if present
+ const std::vector<uint8_t>* fde = it->compiled_method_->GetCFIInfo();
+ if (fde != nullptr) {
+ // Copy the information into cfi_info and then fix the address in the new copy.
+ int cur_offset = cfi_info->size();
+ cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
+
+ bool is_64bit = *(reinterpret_cast<const uint32_t*>(fde->data())) == 0xffffffff;
+
+ // Set the 'CIE_pointer' field.
+ uint64_t CIE_pointer = cur_offset + (is_64bit ? 12 : 4);
+ uint64_t offset_to_update = CIE_pointer;
+ if (is_64bit) {
+ (*cfi_info)[offset_to_update+0] = CIE_pointer;
+ (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
+ (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
+ (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
+ (*cfi_info)[offset_to_update+4] = CIE_pointer >> 32;
+ (*cfi_info)[offset_to_update+5] = CIE_pointer >> 40;
+ (*cfi_info)[offset_to_update+6] = CIE_pointer >> 48;
+ (*cfi_info)[offset_to_update+7] = CIE_pointer >> 56;
+ } else {
+ (*cfi_info)[offset_to_update+0] = CIE_pointer;
+ (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
+ (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
+ (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
+ }
+
+ // Set the 'initial_location' field.
+ offset_to_update += is_64bit ? 8 : 4;
+ if (is_64bit) {
+ const uint64_t quick_code_start = it->low_pc_ + text_section_address;
+ (*cfi_info)[offset_to_update+0] = quick_code_start;
+ (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
+ (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
+ (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
+ (*cfi_info)[offset_to_update+4] = quick_code_start >> 32;
+ (*cfi_info)[offset_to_update+5] = quick_code_start >> 40;
+ (*cfi_info)[offset_to_update+6] = quick_code_start >> 48;
+ (*cfi_info)[offset_to_update+7] = quick_code_start >> 56;
+ } else {
+ const uint32_t quick_code_start = it->low_pc_ + text_section_address;
+ (*cfi_info)[offset_to_update+0] = quick_code_start;
+ (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
+ (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
+ (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
+ }
+ }
+ }
+ }
+
+ bool hasCFI = (cfi_info.get() != nullptr);
+ bool hasLineInfo = false;
+ for (auto& dbg_info : oat_writer->GetCFIMethodInfo()) {
+ if (dbg_info.dbgstream_ != nullptr &&
+ !dbg_info.compiled_method_->GetSrcMappingTable().empty()) {
+ hasLineInfo = true;
+ break;
+ }
+ }
+
+ if (hasLineInfo || hasCFI) {
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_info(".debug_info",
+ SHT_PROGBITS,
+ 0, nullptr, 0, 1, 0);
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_abbrev(".debug_abbrev",
+ SHT_PROGBITS,
+ 0, nullptr, 0, 1, 0);
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_str(".debug_str",
+ SHT_PROGBITS,
+ 0, nullptr, 0, 1, 0);
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_line(".debug_line",
+ SHT_PROGBITS,
+ 0, nullptr, 0, 1, 0);
+
+ FillInCFIInformation(oat_writer, debug_info.GetBuffer(),
+ debug_abbrev.GetBuffer(), debug_str.GetBuffer(),
+ hasLineInfo ? debug_line.GetBuffer() : nullptr,
+ text_section_address);
+
+ builder->RegisterRawSection(debug_info);
+ builder->RegisterRawSection(debug_abbrev);
+
+ if (hasCFI) {
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> eh_frame(".eh_frame",
+ SHT_PROGBITS,
+ SHF_ALLOC,
+ nullptr, 0, 4, 0);
+ eh_frame.SetBuffer(std::move(*cfi_info.get()));
+ builder->RegisterRawSection(eh_frame);
+ }
+
+ if (hasLineInfo) {
+ builder->RegisterRawSection(debug_line);
+ }
+
+ builder->RegisterRawSection(debug_str);
+ }
+}
+
// Explicit instantiations
template class ElfWriterQuick<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>;
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
index 890528e..4990ed0 100644
--- a/compiler/elf_writer_quick.h
+++ b/compiler/elf_writer_quick.h
@@ -19,7 +19,6 @@
#include "elf_utils.h"
#include "elf_writer.h"
-#include "instruction_set.h"
namespace art {
@@ -50,300 +49,6 @@
: ElfWriter(driver, elf_file) {}
~ElfWriterQuick() {}
- class ElfBuilder;
- void WriteDebugSymbols(ElfBuilder* builder, OatWriter* oat_writer);
- void ReservePatchSpace(std::vector<uint8_t>* buffer, bool debug);
-
- class ElfSectionBuilder {
- public:
- ElfSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
- const ElfSectionBuilder *link, Elf_Word info, Elf_Word align,
- Elf_Word entsize)
- : name_(sec_name), link_(link) {
- memset(§ion_, 0, sizeof(section_));
- section_.sh_type = type;
- section_.sh_flags = flags;
- section_.sh_info = info;
- section_.sh_addralign = align;
- section_.sh_entsize = entsize;
- }
-
- virtual ~ElfSectionBuilder() {}
-
- Elf_Shdr section_;
- Elf_Word section_index_ = 0;
-
- protected:
- const std::string name_;
- const ElfSectionBuilder* link_;
-
- Elf_Word GetLink() {
- return (link_) ? link_->section_index_ : 0;
- }
-
- private:
- friend class ElfBuilder;
- };
-
- class ElfDynamicBuilder : public ElfSectionBuilder {
- public:
- void AddDynamicTag(Elf_Sword tag, Elf_Word d_un);
- void AddDynamicTag(Elf_Sword tag, Elf_Word offset, ElfSectionBuilder* section);
-
- ElfDynamicBuilder(const std::string& sec_name, ElfSectionBuilder *link)
- : ElfSectionBuilder(sec_name, SHT_DYNAMIC, SHF_ALLOC | SHF_ALLOC, link,
- 0, kPageSize, sizeof(Elf_Dyn)) {}
- ~ElfDynamicBuilder() {}
-
- protected:
- struct ElfDynamicState {
- ElfSectionBuilder* section_;
- Elf_Sword tag_;
- Elf_Word off_;
- };
- std::vector<ElfDynamicState> dynamics_;
- Elf_Word GetSize() {
- // Add 1 for the DT_NULL, 1 for DT_STRSZ, and 1 for DT_SONAME. All of
- // these must be added when we actually put the file together because
- // their values are very dependent on state.
- return dynamics_.size() + 3;
- }
-
- // Create the actual dynamic vector. strsz should be the size of the .dynstr
- // table and soname_off should be the offset of the soname in .dynstr.
- // Since niether can be found prior to final layout we will wait until here
- // to add them.
- std::vector<Elf_Dyn> GetDynamics(Elf_Word strsz, Elf_Word soname_off);
-
- private:
- friend class ElfBuilder;
- };
-
- class ElfRawSectionBuilder : public ElfSectionBuilder {
- public:
- ElfRawSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
- const ElfSectionBuilder* link, Elf_Word info, Elf_Word align,
- Elf_Word entsize)
- : ElfSectionBuilder(sec_name, type, flags, link, info, align, entsize) {}
- ~ElfRawSectionBuilder() {}
- std::vector<uint8_t>* GetBuffer() { return &buf_; }
- void SetBuffer(std::vector<uint8_t>&& buf) { buf_ = buf; }
-
- protected:
- std::vector<uint8_t> buf_;
-
- private:
- friend class ElfBuilder;
- };
-
- class ElfOatSectionBuilder : public ElfSectionBuilder {
- public:
- ElfOatSectionBuilder(const std::string& sec_name, Elf_Word size, Elf_Word offset,
- Elf_Word type, Elf_Word flags)
- : ElfSectionBuilder(sec_name, type, flags, NULL, 0, kPageSize, 0),
- offset_(offset), size_(size) {}
- ~ElfOatSectionBuilder() {}
-
- protected:
- // Offset of the content within the file.
- Elf_Word offset_;
- // Size of the content within the file.
- Elf_Word size_;
-
- private:
- friend class ElfBuilder;
- };
-
- class ElfSymtabBuilder : public ElfSectionBuilder {
- public:
- // Add a symbol with given name to this symtab. The symbol refers to
- // 'relative_addr' within the given section and has the given attributes.
- void AddSymbol(const std::string& name,
- const ElfSectionBuilder* section,
- Elf_Addr addr,
- bool is_relative,
- Elf_Word size,
- uint8_t binding,
- uint8_t type,
- uint8_t other = 0);
-
- ElfSymtabBuilder(const std::string& sec_name, Elf_Word type,
- const std::string& str_name, Elf_Word str_type, bool alloc)
- : ElfSectionBuilder(sec_name, type, ((alloc) ? SHF_ALLOC : 0U), &strtab_, 0,
- sizeof(Elf_Word), sizeof(Elf_Sym)),
- str_name_(str_name), str_type_(str_type),
- strtab_(str_name, str_type, ((alloc) ? SHF_ALLOC : 0U), NULL, 0, 1, 1) {}
- ~ElfSymtabBuilder() {}
-
- protected:
- std::vector<Elf_Word> GenerateHashContents();
- std::string GenerateStrtab();
- std::vector<Elf_Sym> GenerateSymtab();
-
- Elf_Word GetSize() {
- // 1 is for the implicit NULL symbol.
- return symbols_.size() + 1;
- }
-
- struct ElfSymbolState {
- const std::string name_;
- const ElfSectionBuilder* section_;
- Elf_Addr addr_;
- Elf_Word size_;
- bool is_relative_;
- uint8_t info_;
- uint8_t other_;
- // Used during Write() to temporarially hold name index in the strtab.
- Elf_Word name_idx_;
- };
-
- // Information for the strsym for dynstr sections.
- const std::string str_name_;
- Elf_Word str_type_;
- // The symbols in the same order they will be in the symbol table.
- std::vector<ElfSymbolState> symbols_;
- ElfSectionBuilder strtab_;
-
- private:
- friend class ElfBuilder;
- };
-
- class ElfBuilder FINAL {
- public:
- ElfBuilder(OatWriter* oat_writer,
- File* elf_file,
- InstructionSet isa,
- Elf_Word rodata_relative_offset,
- Elf_Word rodata_size,
- Elf_Word text_relative_offset,
- Elf_Word text_size,
- const bool add_symbols,
- bool debug = false)
- : oat_writer_(oat_writer),
- elf_file_(elf_file),
- add_symbols_(add_symbols),
- debug_logging_(debug),
- text_builder_(".text", text_size, text_relative_offset, SHT_PROGBITS,
- SHF_ALLOC | SHF_EXECINSTR),
- rodata_builder_(".rodata", rodata_size, rodata_relative_offset,
- SHT_PROGBITS, SHF_ALLOC),
- dynsym_builder_(".dynsym", SHT_DYNSYM, ".dynstr", SHT_STRTAB, true),
- symtab_builder_(".symtab", SHT_SYMTAB, ".strtab", SHT_STRTAB, false),
- hash_builder_(".hash", SHT_HASH, SHF_ALLOC, &dynsym_builder_, 0,
- sizeof(Elf_Word), sizeof(Elf_Word)),
- dynamic_builder_(".dynamic", &dynsym_builder_),
- shstrtab_builder_(".shstrtab", SHT_STRTAB, 0, NULL, 0, 1, 1) {
- SetupEhdr();
- SetupDynamic();
- SetupRequiredSymbols();
- SetISA(isa);
- }
- ~ElfBuilder() {}
-
- bool Init();
- bool Write();
-
- // Adds the given raw section to the builder. This will copy it. The caller
- // is responsible for deallocating their copy.
- void RegisterRawSection(ElfRawSectionBuilder bld) {
- other_builders_.push_back(bld);
- }
-
- private:
- OatWriter* oat_writer_;
- File* elf_file_;
- const bool add_symbols_;
- const bool debug_logging_;
-
- bool fatal_error_ = false;
-
- // What phdr is.
- static const uint32_t PHDR_OFFSET = sizeof(Elf_Ehdr);
- enum : uint8_t {
- PH_PHDR = 0,
- PH_LOAD_R__ = 1,
- PH_LOAD_R_X = 2,
- PH_LOAD_RW_ = 3,
- PH_DYNAMIC = 4,
- PH_NUM = 5,
- };
- static const uint32_t PHDR_SIZE = sizeof(Elf_Phdr) * PH_NUM;
- Elf_Phdr program_headers_[PH_NUM];
-
- Elf_Ehdr elf_header_;
-
- Elf_Shdr null_hdr_;
- std::string shstrtab_;
- uint32_t section_index_;
- std::string dynstr_;
- uint32_t dynstr_soname_offset_;
- std::vector<Elf_Shdr*> section_ptrs_;
- std::vector<Elf_Word> hash_;
-
- public:
- ElfOatSectionBuilder text_builder_;
- ElfOatSectionBuilder rodata_builder_;
- ElfSymtabBuilder dynsym_builder_;
- ElfSymtabBuilder symtab_builder_;
- ElfSectionBuilder hash_builder_;
- ElfDynamicBuilder dynamic_builder_;
- ElfSectionBuilder shstrtab_builder_;
- std::vector<ElfRawSectionBuilder> other_builders_;
-
- private:
- void SetISA(InstructionSet isa);
- void SetupEhdr();
-
- // Sets up a bunch of the required Dynamic Section entries.
- // Namely it will initialize all the mandatory ones that it can.
- // Specifically:
- // DT_HASH
- // DT_STRTAB
- // DT_SYMTAB
- // DT_SYMENT
- //
- // Some such as DT_SONAME, DT_STRSZ and DT_NULL will be put in later.
- void SetupDynamic();
-
- // Sets up the basic dynamic symbols that are needed, namely all those we
- // can know already.
- //
- // Specifically adds:
- // oatdata
- // oatexec
- // oatlastword
- void SetupRequiredSymbols();
- void AssignSectionStr(ElfSectionBuilder *builder, std::string* strtab);
- struct ElfFilePiece {
- ElfFilePiece(const std::string& name, Elf_Word offset, const void* data, Elf_Word size)
- : dbg_name_(name), offset_(offset), data_(data), size_(size) {}
- ~ElfFilePiece() {}
-
- const std::string& dbg_name_;
- Elf_Word offset_;
- const void *data_;
- Elf_Word size_;
- static bool Compare(ElfFilePiece a, ElfFilePiece b) {
- return a.offset_ < b.offset_;
- }
- };
-
- // Write each of the pieces out to the file.
- bool WriteOutFile(const std::vector<ElfFilePiece>& pieces);
- bool IncludingDebugSymbols() { return add_symbols_ && symtab_builder_.GetSize() > 1; }
- };
-
- /*
- * @brief Generate the DWARF debug_info and debug_abbrev sections
- * @param oat_writer The Oat file Writer.
- * @param dbg_info Compilation unit information.
- * @param dbg_abbrev Abbreviations used to generate dbg_info.
- * @param dbg_str Debug strings.
- */
- void FillInCFIInformation(OatWriter* oat_writer, std::vector<uint8_t>* dbg_info,
- std::vector<uint8_t>* dbg_abbrev, std::vector<uint8_t>* dbg_str,
- std::vector<uint8_t>* dbg_line, uint32_t text_section_offset);
-
DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick);
};
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index c35ff85..25d10bd 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -22,17 +22,17 @@
oatdump.cc
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler,art/disassembler,target,ndebug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler libart-compiler,art/disassembler art/compiler,target,ndebug))
endif
ifeq ($(ART_BUILD_TARGET_DEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler,art/disassembler,target,debug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler libart-compiler,art/disassembler art/compiler,target,debug))
endif
ifeq ($(ART_BUILD_HOST_NDEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler,art/disassembler,host,ndebug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler libart-compiler,art/disassembler art/compiler,host,ndebug))
endif
ifeq ($(ART_BUILD_HOST_DEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler,art/disassembler,host,debug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler libart-compiler,art/disassembler art/compiler,host,debug))
endif
########################################################################
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 1feb27d..fff42f6 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -20,6 +20,7 @@
#include <fstream>
#include <iostream>
#include <string>
+#include <unordered_map>
#include <vector>
#include "base/stringpiece.h"
@@ -29,6 +30,7 @@
#include "dex_file-inl.h"
#include "dex_instruction.h"
#include "disassembler.h"
+#include "elf_builder.h"
#include "field_helper.h"
#include "gc_map.h"
#include "gc/space/image_space.h"
@@ -47,6 +49,7 @@
#include "oat.h"
#include "oat_file-inl.h"
#include "os.h"
+#include "output_stream.h"
#include "runtime.h"
#include "safe_map.h"
#include "scoped_thread_state_change.h"
@@ -105,6 +108,224 @@
"kClassRoots",
};
+class OatSymbolizer : public CodeOutput {
+ public:
+ explicit OatSymbolizer(const OatFile* oat_file, std::string& output_name) :
+ oat_file_(oat_file), builder_(nullptr), elf_output_(nullptr), output_name_(output_name) {}
+
+ bool Init() {
+ Elf32_Word oat_data_size = oat_file_->GetOatHeader().GetExecutableOffset();
+
+ uint32_t diff = static_cast<uint32_t>(oat_file_->End() - oat_file_->Begin());
+ uint32_t oat_exec_size = diff - oat_data_size;
+
+ if (output_name_.empty()) {
+ output_name_ = "symbolized.oat";
+ }
+ elf_output_ = OS::CreateEmptyFile(output_name_.c_str());
+
+ builder_.reset(new ElfBuilder<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+ Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(
+ this,
+ elf_output_,
+ oat_file_->GetOatHeader().GetInstructionSet(),
+ 0,
+ oat_data_size,
+ oat_data_size,
+ oat_exec_size,
+ true,
+ false));
+
+ if (!builder_->Init()) {
+ builder_.reset(nullptr);
+ return false;
+ }
+
+ return true;
+ }
+
+ typedef void (OatSymbolizer::*Callback)(const DexFile::ClassDef&,
+ uint32_t,
+ const OatFile::OatMethod&,
+ const DexFile&,
+ uint32_t,
+ const DexFile::CodeItem*,
+ uint32_t);
+
+ bool Symbolize() {
+ if (builder_.get() == nullptr) {
+ return false;
+ }
+
+ Walk(&art::OatSymbolizer::RegisterForDedup);
+
+ NormalizeState();
+
+ Walk(&art::OatSymbolizer::AddSymbol);
+
+ bool result = builder_->Write();
+
+ elf_output_->Flush();
+ elf_output_->Close();
+
+ return result;
+ }
+
+ void Walk(Callback callback) {
+ std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file_->GetOatDexFiles();
+ for (size_t i = 0; i < oat_dex_files.size(); i++) {
+ const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i];
+ CHECK(oat_dex_file != NULL);
+ WalkOatDexFile(oat_dex_file, callback);
+ }
+ }
+
+ void WalkOatDexFile(const OatFile::OatDexFile* oat_dex_file, Callback callback) {
+ std::string error_msg;
+ std::unique_ptr<const DexFile> dex_file(oat_dex_file->OpenDexFile(&error_msg));
+ if (dex_file.get() == nullptr) {
+ return;
+ }
+ for (size_t class_def_index = 0;
+ class_def_index < dex_file->NumClassDefs();
+ class_def_index++) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+ const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
+ OatClassType type = oat_class.GetType();
+ switch (type) {
+ case kOatClassAllCompiled:
+ case kOatClassSomeCompiled:
+ WalkOatClass(oat_class, *dex_file.get(), class_def, callback);
+ break;
+
+ case kOatClassNoneCompiled:
+ case kOatClassMax:
+ // Ignore.
+ break;
+ }
+ }
+ }
+
+ void WalkOatClass(const OatFile::OatClass& oat_class, const DexFile& dex_file,
+ const DexFile::ClassDef& class_def, Callback callback) {
+ const byte* class_data = dex_file.GetClassData(class_def);
+ if (class_data == nullptr) { // empty class such as a marker interface?
+ return;
+ }
+ // Note: even if this is an interface or a native class, we still have to walk it, as there
+ // might be a static initializer.
+ ClassDataItemIterator it(dex_file, class_data);
+ SkipAllFields(&it);
+ uint32_t class_method_idx = 0;
+ while (it.HasNextDirectMethod()) {
+ const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
+ WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(),
+ it.GetMethodCodeItem(), it.GetMemberAccessFlags(), callback);
+ class_method_idx++;
+ it.Next();
+ }
+ while (it.HasNextVirtualMethod()) {
+ const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
+ WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(),
+ it.GetMethodCodeItem(), it.GetMemberAccessFlags(), callback);
+ class_method_idx++;
+ it.Next();
+ }
+ DCHECK(!it.HasNext());
+ }
+
+ void WalkOatMethod(const DexFile::ClassDef& class_def, uint32_t class_method_index,
+ const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+ uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+ uint32_t method_access_flags, Callback callback) {
+ if ((method_access_flags & kAccAbstract) != 0) {
+ // Abstract method, no code.
+ return;
+ }
+ if (oat_method.GetCodeOffset() == 0) {
+ // No code.
+ return;
+ }
+
+ (this->*callback)(class_def, class_method_index, oat_method, dex_file, dex_method_idx, code_item,
+ method_access_flags);
+ }
+
+ void RegisterForDedup(const DexFile::ClassDef& class_def, uint32_t class_method_index,
+ const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+ uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+ uint32_t method_access_flags) {
+ state_[oat_method.GetCodeOffset()]++;
+ }
+
+ void NormalizeState() {
+ for (auto& x : state_) {
+ if (x.second == 1) {
+ state_[x.first] = 0;
+ }
+ }
+ }
+
+ enum class DedupState { // private
+ kNotDeduplicated,
+ kDeduplicatedFirst,
+ kDeduplicatedOther
+ };
+ DedupState IsDuplicated(uint32_t offset) {
+ if (state_[offset] == 0) {
+ return DedupState::kNotDeduplicated;
+ }
+ if (state_[offset] == 1) {
+ return DedupState::kDeduplicatedOther;
+ }
+ state_[offset] = 1;
+ return DedupState::kDeduplicatedFirst;
+ }
+
+ void AddSymbol(const DexFile::ClassDef& class_def, uint32_t class_method_index,
+ const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+ uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+ uint32_t method_access_flags) {
+ DedupState dedup = IsDuplicated(oat_method.GetCodeOffset());
+ if (dedup != DedupState::kDeduplicatedOther) {
+ std::string pretty_name = PrettyMethod(dex_method_idx, dex_file, true);
+
+ if (dedup == DedupState::kDeduplicatedFirst) {
+ pretty_name = "[Dedup]" + pretty_name;
+ }
+
+ ElfSymtabBuilder<Elf32_Word, Elf32_Sword, Elf32_Addr,
+ Elf32_Sym, Elf32_Shdr>* symtab = &builder_->symtab_builder_;
+
+ symtab->AddSymbol(pretty_name, &builder_->text_builder_, oat_method.GetCodeOffset() -
+ oat_file_->GetOatHeader().GetExecutableOffset(), true,
+ oat_method.GetQuickCodeSize(), STB_GLOBAL, STT_FUNC);
+ }
+ }
+
+ // Write oat code. Required by ElfBuilder/CodeOutput.
+ bool Write(OutputStream* out) {
+ return out->WriteFully(oat_file_->Begin(), oat_file_->End() - oat_file_->Begin());
+ }
+
+ private:
+ static void SkipAllFields(ClassDataItemIterator* it) {
+ while (it->HasNextStaticField()) {
+ it->Next();
+ }
+ while (it->HasNextInstanceField()) {
+ it->Next();
+ }
+ }
+
+ const OatFile* oat_file_;
+ std::unique_ptr<ElfBuilder<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+ Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr> > builder_;
+ File* elf_output_;
+ std::unordered_map<uint32_t, uint32_t> state_;
+ std::string output_name_;
+};
+
class OatDumper {
public:
explicit OatDumper(const OatFile& oat_file, bool dump_raw_mapping_table, bool dump_raw_gc_map)
@@ -396,6 +617,7 @@
*indent2_os << StringPrintf("\nvmap_table: %p (offset=0x%08x)\n",
oat_method.GetVmapTable(), oat_method.GetVmapTableOffset());
DumpVmap(*indent2_os, oat_method);
+ DumpVregLocations(*indent2_os, oat_method, code_item);
*indent2_os << StringPrintf("mapping_table: %p (offset=0x%08x)\n",
oat_method.GetMappingTable(), oat_method.GetMappingTableOffset());
if (dump_raw_mapping_table_) {
@@ -492,6 +714,45 @@
}
}
+ void DumpVregLocations(std::ostream& os, const OatFile::OatMethod& oat_method,
+ const DexFile::CodeItem* code_item) {
+ if (code_item != nullptr) {
+ size_t num_locals_ins = code_item->registers_size_;
+ size_t num_ins = code_item->ins_size_;
+ size_t num_locals = num_locals_ins - num_ins;
+ size_t num_outs = code_item->outs_size_;
+
+ os << "vr_stack_locations:";
+ for (size_t reg = 0; reg <= num_locals_ins; reg++) {
+ // For readability, delimit the different kinds of VRs.
+ if (reg == num_locals_ins) {
+ os << "\n\tmethod*:";
+ } else if (reg == num_locals && num_ins > 0) {
+ os << "\n\tins:";
+ } else if (reg == 0 && num_locals > 0) {
+ os << "\n\tlocals:";
+ }
+
+ uint32_t offset = StackVisitor::GetVRegOffset(code_item, oat_method.GetCoreSpillMask(),
+ oat_method.GetFpSpillMask(),
+ oat_method.GetFrameSizeInBytes(), reg,
+ GetInstructionSet());
+ os << " v" << reg << "[sp + #" << offset << "]";
+ }
+
+ for (size_t out_reg = 0; out_reg < num_outs; out_reg++) {
+ if (out_reg == 0) {
+ os << "\n\touts:";
+ }
+
+ uint32_t offset = StackVisitor::GetOutVROffset(out_reg, GetInstructionSet());
+ os << " v" << out_reg << "[sp + #" << offset << "]";
+ }
+
+ os << "\n";
+ }
+ }
+
void DescribeVReg(std::ostream& os, const OatFile::OatMethod& oat_method,
const DexFile::CodeItem* code_item, size_t reg, VRegKind kind) {
const uint8_t* raw_table = oat_method.GetVmapTable();
@@ -1497,8 +1758,10 @@
std::string elf_filename_prefix;
std::ostream* os = &std::cout;
std::unique_ptr<std::ofstream> out;
+ std::string output_name;
bool dump_raw_mapping_table = false;
bool dump_raw_gc_map = false;
+ bool symbolize = false;
for (int i = 0; i < argc; i++) {
const StringPiece option(argv[i]);
@@ -1531,13 +1794,17 @@
usage();
}
} else if (option.starts_with("--output=")) {
- const char* filename = option.substr(strlen("--output=")).data();
+ output_name = option.substr(strlen("--output=")).ToString();
+ const char* filename = output_name.c_str();
out.reset(new std::ofstream(filename));
if (!out->good()) {
fprintf(stderr, "Failed to open output filename %s\n", filename);
usage();
}
os = out.get();
+ } else if (option.starts_with("--symbolize=")) {
+ oat_filename = option.substr(strlen("--symbolize=")).data();
+ symbolize = true;
} else {
fprintf(stderr, "Unknown argument %s\n", option.data());
usage();
@@ -1562,8 +1829,20 @@
fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
}
- OatDumper oat_dumper(*oat_file, dump_raw_mapping_table, dump_raw_gc_map);
- oat_dumper.Dump(*os);
+ if (symbolize) {
+ OatSymbolizer oat_symbolizer(oat_file, output_name);
+ if (!oat_symbolizer.Init()) {
+ fprintf(stderr, "Failed to initialize symbolizer\n");
+ return EXIT_FAILURE;
+ }
+ if (!oat_symbolizer.Symbolize()) {
+ fprintf(stderr, "Failed to symbolize\n");
+ return EXIT_FAILURE;
+ }
+ } else {
+ OatDumper oat_dumper(*oat_file, dump_raw_mapping_table, dump_raw_gc_map);
+ oat_dumper.Dump(*os);
+ }
return EXIT_SUCCESS;
}
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 337e5fe..4155b7e 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1255,6 +1255,7 @@
PUSH ecx // Pass receiver.
PUSH eax // Pass Method*.
call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP, LR)
+ SETUP_GOT_NOSAVE
addl LITERAL(28), %esp // Pop arguments upto saved Method*.
movl 28(%esp), %edi // Restore edi.
movl %eax, 28(%esp) // Place code* over edi, just under return pc.
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 23caefc..f01ea0c 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -30,22 +30,24 @@
namespace art {
Mutex* Locks::abort_lock_ = nullptr;
+Mutex* Locks::alloc_tracker_lock_ = nullptr;
Mutex* Locks::allocated_monitor_ids_lock_ = nullptr;
Mutex* Locks::allocated_thread_ids_lock_ = nullptr;
ReaderWriterMutex* Locks::breakpoint_lock_ = nullptr;
ReaderWriterMutex* Locks::classlinker_classes_lock_ = nullptr;
+Mutex* Locks::deoptimization_lock_ = nullptr;
ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr;
Mutex* Locks::jni_libraries_lock_ = nullptr;
Mutex* Locks::logging_lock_ = nullptr;
Mutex* Locks::mem_maps_lock_ = nullptr;
Mutex* Locks::modify_ldt_lock_ = nullptr;
ReaderWriterMutex* Locks::mutator_lock_ = nullptr;
+Mutex* Locks::profiler_lock_ = nullptr;
Mutex* Locks::runtime_shutdown_lock_ = nullptr;
Mutex* Locks::thread_list_lock_ = nullptr;
Mutex* Locks::thread_list_suspend_thread_lock_ = nullptr;
Mutex* Locks::thread_suspend_count_lock_ = nullptr;
Mutex* Locks::trace_lock_ = nullptr;
-Mutex* Locks::profiler_lock_ = nullptr;
Mutex* Locks::unexpected_signal_lock_ = nullptr;
Mutex* Locks::intern_table_lock_ = nullptr;
@@ -830,21 +832,23 @@
DCHECK(modify_ldt_lock_ == nullptr);
}
DCHECK(abort_lock_ != nullptr);
+ DCHECK(alloc_tracker_lock_ != nullptr);
DCHECK(allocated_monitor_ids_lock_ != nullptr);
DCHECK(allocated_thread_ids_lock_ != nullptr);
DCHECK(breakpoint_lock_ != nullptr);
DCHECK(classlinker_classes_lock_ != nullptr);
+ DCHECK(deoptimization_lock_ != nullptr);
DCHECK(heap_bitmap_lock_ != nullptr);
+ DCHECK(intern_table_lock_ != nullptr);
DCHECK(jni_libraries_lock_ != nullptr);
DCHECK(logging_lock_ != nullptr);
DCHECK(mutator_lock_ != nullptr);
+ DCHECK(profiler_lock_ != nullptr);
DCHECK(thread_list_lock_ != nullptr);
DCHECK(thread_list_suspend_thread_lock_ != nullptr);
DCHECK(thread_suspend_count_lock_ != nullptr);
DCHECK(trace_lock_ != nullptr);
- DCHECK(profiler_lock_ != nullptr);
DCHECK(unexpected_signal_lock_ != nullptr);
- DCHECK(intern_table_lock_ != nullptr);
} else {
// Create global locks in level order from highest lock level to lowest.
LockLevel current_lock_level = kThreadListSuspendThreadLock;
@@ -853,7 +857,12 @@
new Mutex("thread list suspend thread by .. lock", current_lock_level);
#define UPDATE_CURRENT_LOCK_LEVEL(new_level) \
- DCHECK_LT(new_level, current_lock_level); \
+ if (new_level >= current_lock_level) { \
+ /* Do not use CHECKs or FATAL here, abort_lock_ is not setup yet. */ \
+ fprintf(stderr, "New local level %d is not less than current level %d\n", \
+ new_level, current_lock_level); \
+ exit(1); \
+ } \
current_lock_level = new_level;
UPDATE_CURRENT_LOCK_LEVEL(kMutatorLock);
@@ -876,6 +885,14 @@
DCHECK(trace_lock_ == nullptr);
trace_lock_ = new Mutex("trace lock", current_lock_level);
+ UPDATE_CURRENT_LOCK_LEVEL(kDeoptimizationLock);
+ DCHECK(deoptimization_lock_ == nullptr);
+ deoptimization_lock_ = new Mutex("Deoptimization lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kAllocTrackerLock);
+ DCHECK(alloc_tracker_lock_ == nullptr);
+ alloc_tracker_lock_ = new Mutex("AllocTracker lock", current_lock_level);
+
UPDATE_CURRENT_LOCK_LEVEL(kThreadListLock);
DCHECK(thread_list_lock_ == nullptr);
thread_list_lock_ = new Mutex("thread list lock", current_lock_level);
@@ -911,7 +928,6 @@
DCHECK(intern_table_lock_ == nullptr);
intern_table_lock_ = new Mutex("InternTable lock", current_lock_level);
-
UPDATE_CURRENT_LOCK_LEVEL(kAbortLock);
DCHECK(abort_lock_ == nullptr);
abort_lock_ = new Mutex("abort lock", current_lock_level, true);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 2a623fd..6642b1e 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -85,6 +85,7 @@
kJniLoadLibraryLock,
kThreadListLock,
kBreakpointInvokeLock,
+ kAllocTrackerLock,
kDeoptimizationLock,
kTraceLock,
kProfilerLock,
@@ -557,9 +558,17 @@
// Guards trace (ie traceview) requests.
static Mutex* trace_lock_ ACQUIRED_AFTER(profiler_lock_);
+ // Guards debugger recent allocation records.
+ static Mutex* alloc_tracker_lock_ ACQUIRED_AFTER(trace_lock_);
+
+ // Guards updates to instrumentation to ensure mutual exclusion of
+ // events like deoptimization requests.
+ // TODO: improve name, perhaps instrumentation_update_lock_.
+ static Mutex* deoptimization_lock_ ACQUIRED_AFTER(alloc_tracker_lock_);
+
// The thread_list_lock_ guards ThreadList::list_. It is also commonly held to stop threads
// attaching and detaching.
- static Mutex* thread_list_lock_ ACQUIRED_AFTER(trace_lock_);
+ static Mutex* thread_list_lock_ ACQUIRED_AFTER(deoptimization_lock_);
// Guards maintaining loading library data structures.
static Mutex* jni_libraries_lock_ ACQUIRED_AFTER(thread_list_lock_);
@@ -586,7 +595,7 @@
static Mutex* intern_table_lock_ ACQUIRED_AFTER(modify_ldt_lock_);
// Have an exclusive aborting thread.
- static Mutex* abort_lock_ ACQUIRED_AFTER(classlinker_classes_lock_);
+ static Mutex* abort_lock_ ACQUIRED_AFTER(intern_table_lock_);
// Allow mutual exclusion when manipulating Thread::suspend_count_.
// TODO: Does the trade-off of a per-thread lock make sense?
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0746e0c..1e64c00 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -785,7 +785,19 @@
return NULL;
}
-static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file, const char* dex_location,
+
+// Loads all multi dex files from the given oat file returning true on success.
+//
+// Parameters:
+// oat_file - the oat file to load from
+// dex_location - the dex location used to generate the oat file
+// dex_location_checksum - the checksum of the dex_location (may be null for pre-opted files)
+// generated - whether or not the oat_file existed before or was just (re)generated
+// error_msgs - any error messages will be appended here
+// dex_files - the loaded dex_files will be appended here (only if the loading succeeds)
+static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file,
+ const char* dex_location,
+ const uint32_t* dex_location_checksum,
bool generated,
std::vector<std::string>* error_msgs,
std::vector<const DexFile*>* dex_files) {
@@ -800,12 +812,20 @@
std::string next_name_str = DexFile::GetMultiDexClassesDexName(i, dex_location);
const char* next_name = next_name_str.c_str();
- uint32_t dex_location_checksum;
- uint32_t* dex_location_checksum_pointer = &dex_location_checksum;
+ uint32_t next_location_checksum;
+ uint32_t* next_location_checksum_pointer = &next_location_checksum;
std::string error_msg;
- if (!DexFile::GetChecksum(next_name, dex_location_checksum_pointer, &error_msg)) {
+ if ((i == 0) && (strcmp(next_name, dex_location) == 0)) {
+ // When i=0 the multidex name should be the same as the location name. We already have the
+ // checksum it so we don't need to recompute it.
+ if (dex_location_checksum == nullptr) {
+ next_location_checksum_pointer = nullptr;
+ } else {
+ next_location_checksum = *dex_location_checksum;
+ }
+ } else if (!DexFile::GetChecksum(next_name, next_location_checksum_pointer, &error_msg)) {
DCHECK_EQ(false, i == 0 && generated);
- dex_location_checksum_pointer = nullptr;
+ next_location_checksum_pointer = nullptr;
}
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(next_name, nullptr, false);
@@ -814,7 +834,7 @@
if (i == 0 && generated) {
std::string error_msg;
error_msg = StringPrintf("\nFailed to find dex file '%s' (checksum 0x%x) in generated out "
- " file'%s'", dex_location, dex_location_checksum,
+ " file'%s'", dex_location, next_location_checksum,
oat_file->GetLocation().c_str());
error_msgs->push_back(error_msg);
}
@@ -823,8 +843,8 @@
// Checksum test. Test must succeed when generated.
success = !generated;
- if (dex_location_checksum_pointer != nullptr) {
- success = dex_location_checksum == oat_dex_file->GetDexFileLocationChecksum();
+ if (next_location_checksum_pointer != nullptr) {
+ success = next_location_checksum == oat_dex_file->GetDexFileLocationChecksum();
}
if (success) {
@@ -840,7 +860,7 @@
// When we generated the file, we expect success, or something is terribly wrong.
CHECK_EQ(false, generated && !success)
<< "dex_location=" << next_name << " oat_location=" << oat_file->GetLocation().c_str()
- << std::hex << " dex_location_checksum=" << dex_location_checksum
+ << std::hex << " dex_location_checksum=" << next_location_checksum
<< " OatDexFile::GetLocationChecksum()=" << oat_dex_file->GetDexFileLocationChecksum();
}
@@ -877,6 +897,7 @@
bool have_checksum = true;
std::string checksum_error_msg;
if (!DexFile::GetChecksum(dex_location, dex_location_checksum_pointer, &checksum_error_msg)) {
+ // This happens for pre-opted files since the corresponding dex files are no longer on disk.
dex_location_checksum_pointer = nullptr;
have_checksum = false;
}
@@ -942,8 +963,9 @@
// 3) If we have an oat file, check all contained multidex files for our dex_location.
// Note: LoadMultiDexFilesFromOatFile will check for nullptr in the first argument.
- bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, false, error_msgs,
- dex_files);
+ bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
+ dex_location_checksum_pointer,
+ false, error_msgs, dex_files);
if (success) {
const OatFile* oat_file = open_oat_file.release(); // Avoid deleting it.
if (needs_registering) {
@@ -1004,8 +1026,8 @@
}
// Try to load again, but stronger checks.
- success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, true, error_msgs,
- dex_files);
+ success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, dex_location_checksum_pointer,
+ true, error_msgs, dex_files);
if (success) {
RegisterOatFile(open_oat_file.release());
return true;
@@ -1189,24 +1211,18 @@
return false;
}
- if (dex_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) {
- *error_msg = StringPrintf("oat file '%s' mismatch (0x%x) with '%s' (0x%x)",
- oat_file->GetLocation().c_str(),
- oat_dex_file->GetDexFileLocationChecksum(),
- dex_location, dex_location_checksum);
- return false;
- }
+ DCHECK_EQ(dex_location_checksum, oat_dex_file->GetDexFileLocationChecksum());
return true;
}
bool ClassLinker::VerifyOatWithDexFile(const OatFile* oat_file,
const char* dex_location,
+ const uint32_t* dex_location_checksum,
std::string* error_msg) {
CHECK(oat_file != nullptr);
CHECK(dex_location != nullptr);
std::unique_ptr<const DexFile> dex_file;
- uint32_t dex_location_checksum;
- if (!DexFile::GetChecksum(dex_location, &dex_location_checksum, error_msg)) {
+ if (dex_location_checksum == nullptr) {
// If no classes.dex found in dex_location, it has been stripped or is corrupt, assume oat is
// up-to-date. This is the common case in user builds for jar's and apk's in the /system
// directory.
@@ -1219,13 +1235,13 @@
}
dex_file.reset(oat_dex_file->OpenDexFile(error_msg));
} else {
- bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, dex_location_checksum,
+ bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, *dex_location_checksum,
kRuntimeISA, error_msg);
if (!verified) {
return false;
}
dex_file.reset(oat_file->GetOatDexFile(dex_location,
- &dex_location_checksum)->OpenDexFile(error_msg));
+ dex_location_checksum)->OpenDexFile(error_msg));
}
return dex_file.get() != nullptr;
}
@@ -1249,7 +1265,8 @@
dex_location));
return nullptr;
} else if (oat_file->IsExecutable() &&
- !VerifyOatWithDexFile(oat_file.get(), dex_location, &error_msg)) {
+ !VerifyOatWithDexFile(oat_file.get(), dex_location,
+ dex_location_checksum, &error_msg)) {
error_msgs->push_back(StringPrintf("Failed to verify oat file '%s' found for dex location "
"'%s': %s", oat_file->GetLocation().c_str(), dex_location,
error_msg.c_str()));
@@ -1804,6 +1821,7 @@
if (dex_cache_image_class_lookup_required_) {
MoveImageClassesToClassTable();
}
+ // TODO: why isn't this a ReaderMutexLock?
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
for (std::pair<const size_t, GcRoot<mirror::Class> >& it : class_table_) {
mirror::Class* c = it.second.Read();
@@ -1813,18 +1831,75 @@
}
}
-static bool GetClassesVisitor(mirror::Class* c, void* arg) {
+static bool GetClassesVisitorSet(mirror::Class* c, void* arg) {
std::set<mirror::Class*>* classes = reinterpret_cast<std::set<mirror::Class*>*>(arg);
classes->insert(c);
return true;
}
+struct GetClassesVisitorArrayArg {
+ Handle<mirror::ObjectArray<mirror::Class>>* classes;
+ int32_t index;
+ bool success;
+};
+
+static bool GetClassesVisitorArray(mirror::Class* c, void* varg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ GetClassesVisitorArrayArg* arg = reinterpret_cast<GetClassesVisitorArrayArg*>(varg);
+ if (arg->index < (*arg->classes)->GetLength()) {
+ (*arg->classes)->Set(arg->index, c);
+ arg->index++;
+ return true;
+ } else {
+ arg->success = false;
+ return false;
+ }
+}
+
void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg) {
- std::set<mirror::Class*> classes;
- VisitClasses(GetClassesVisitor, &classes);
- for (mirror::Class* klass : classes) {
- if (!visitor(klass, arg)) {
- return;
+ // TODO: it may be possible to avoid secondary storage if we iterate over dex caches. The problem
+ // is avoiding duplicates.
+ if (!kMovingClasses) {
+ std::set<mirror::Class*> classes;
+ VisitClasses(GetClassesVisitorSet, &classes);
+ for (mirror::Class* klass : classes) {
+ if (!visitor(klass, arg)) {
+ return;
+ }
+ }
+ } else {
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ObjectArray<mirror::Class>> classes =
+ hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
+ GetClassesVisitorArrayArg local_arg;
+ local_arg.classes = &classes;
+ local_arg.success = false;
+ // We size the array assuming classes won't be added to the class table during the visit.
+ // If this assumption fails we iterate again.
+ while (!local_arg.success) {
+ size_t class_table_size;
+ {
+ ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ class_table_size = class_table_.size();
+ }
+ mirror::Class* class_type = mirror::Class::GetJavaLangClass();
+ mirror::Class* array_of_class = FindArrayClass(self, &class_type);
+ classes.Assign(
+ mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, class_table_size));
+ CHECK(classes.Get() != nullptr); // OOME.
+ local_arg.index = 0;
+ local_arg.success = true;
+ VisitClasses(GetClassesVisitorArray, &local_arg);
+ }
+ for (int32_t i = 0; i < classes->GetLength(); ++i) {
+ // If the class table shrank during creation of the clases array we expect null elements. If
+ // the class table grew then the loop repeats. If classes are created after the loop has
+ // finished then we don't visit.
+ mirror::Class* klass = classes->Get(i);
+ if (klass != nullptr && !visitor(klass, arg)) {
+ return;
+ }
}
}
}
@@ -2309,7 +2384,9 @@
size_t end = declaring_class->NumVirtualMethods();
bool found = false;
for (size_t i = 0; i < end; i++) {
- if (declaring_class->GetVirtualMethod(i) == method) {
+ // Check method index instead of identity in case of duplicate method definitions.
+ if (method->GetDexMethodIndex() ==
+ declaring_class->GetVirtualMethod(i)->GetDexMethodIndex()) {
found = true;
break;
}
@@ -2716,6 +2793,8 @@
klass->SetVirtualMethods(virtuals);
}
size_t class_def_method_index = 0;
+ uint32_t last_dex_method_index = DexFile::kDexNoIndex;
+ size_t last_class_def_method_index = 0;
for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
StackHandleScope<1> hs(self);
Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass)));
@@ -2725,7 +2804,15 @@
}
klass->SetDirectMethod(i, method.Get());
LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
- method->SetMethodIndex(class_def_method_index);
+ uint32_t it_method_index = it.GetMemberIndex();
+ if (last_dex_method_index == it_method_index) {
+ // duplicate case
+ method->SetMethodIndex(last_class_def_method_index);
+ } else {
+ method->SetMethodIndex(class_def_method_index);
+ last_dex_method_index = it_method_index;
+ last_class_def_method_index = class_def_method_index;
+ }
class_def_method_index++;
}
for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index a7a68b7..d1f5aa0 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -237,12 +237,14 @@
}
void VisitClasses(ClassVisitor* visitor, void* arg)
- LOCKS_EXCLUDED(dex_lock_)
+ LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Less efficient variant of VisitClasses that doesn't hold the classlinker_classes_lock_
- // when calling the visitor.
+
+ // Less efficient variant of VisitClasses that copies the class_table_ into secondary storage
+ // so that it can visit individual classes without holding the doesn't hold the
+ // Locks::classlinker_classes_lock_. As the Locks::classlinker_classes_lock_ isn't held this code
+ // can race with insertion and deletion of classes while the visitor is being called.
void VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg)
- LOCKS_EXCLUDED(dex_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void VisitClassRoots(RootCallback* callback, void* arg, VisitRootFlags flags)
@@ -611,9 +613,18 @@
bool* obsolete_file_cleanup_failed)
LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
- // verify an oat file with the given dex file. Will return false when the dex file could not be
- // verified. Will return true otherwise.
+ // Verifies:
+ // - that the oat file contains the dex file (with a matching checksum, which may be null if the
+ // file was pre-opted)
+ // - the checksums of the oat file (against the image space)
+ // - the checksum of the dex file against dex_location_checksum
+ // - that the dex file can be opened
+ // Returns true iff all verification succeed.
+ //
+ // The dex_location is the dex location as stored in the oat file header.
+ // (see DexFile::GetDexCanonicalLocation for a description of location conventions)
bool VerifyOatWithDexFile(const OatFile* oat_file, const char* dex_location,
+ const uint32_t* dex_location_checksum,
std::string* error_msg);
mirror::ArtMethod* CreateProxyConstructor(Thread* self, ConstHandle<mirror::Class> klass,
@@ -623,6 +634,33 @@
ConstHandle<mirror::ArtMethod> prototype)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::Class* LookupClassFromTableLocked(const char* descriptor,
+ const mirror::ClassLoader* class_loader,
+ size_t hash)
+ SHARED_LOCKS_REQUIRED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+
+ mirror::Class* UpdateClass(const char* descriptor, mirror::Class* klass, size_t hash)
+ LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void MoveImageClassesToClassTable() LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
+ LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::Class* LookupClassFromImage(const char* descriptor)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // EnsureResolved is called to make sure that a class in the class_table_ has been resolved
+ // before returning it to the caller. Its the responsibility of the thread that placed the class
+ // in the table to make it resolved. The thread doing resolution must notify on the class' lock
+ // when resolution has occurred. This happens in mirror::Class::SetStatus. As resolution may
+ // retire a class, the version of the class in the table is returned and this may differ from
+ // the class passed in.
+ mirror::Class* EnsureResolved(Thread* self, const char* descriptor, mirror::Class* klass)
+ WARN_UNUSED SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
std::vector<const DexFile*> boot_class_path_;
mutable ReaderWriterMutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
@@ -646,32 +684,6 @@
// the classes into the class_table_ to avoid dex cache based searches.
Atomic<uint32_t> failed_dex_cache_class_lookups_;
- mirror::Class* LookupClassFromTableLocked(const char* descriptor,
- const mirror::ClassLoader* class_loader,
- size_t hash)
- SHARED_LOCKS_REQUIRED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
-
- mirror::Class* UpdateClass(const char* descriptor, mirror::Class* klass, size_t hash)
- LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- void MoveImageClassesToClassTable() LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- mirror::Class* LookupClassFromImage(const char* descriptor)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- // EnsureResolved is called to make sure that a class in the class_table_ has been resolved
- // before returning it to the caller. Its the responsibility of the thread that placed the class
- // in the table to make it resolved. The thread doing resolution must notify on the class' lock
- // when resolution has occurred. This happens in mirror::Class::SetStatus. As resolution may
- // retire a class, the version of the class in the table is returned and this may differ from
- // the class passed in.
- mirror::Class* EnsureResolved(Thread* self, const char* descriptor, mirror::Class* klass)
- WARN_UNUSED SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- void FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
// indexes into class_roots_.
// needs to be kept in sync with class_roots_descriptors_.
enum ClassRoot {
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index b3c887e..488e6e7 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -61,7 +61,15 @@
namespace art {
static const size_t kMaxAllocRecordStackDepth = 16; // Max 255.
-static const size_t kDefaultNumAllocRecords = 64*1024; // Must be a power of 2.
+static const size_t kDefaultNumAllocRecords = 64*1024; // Must be a power of 2. 2BE can hold 64k-1.
+
+// Limit alloc_record_count to the 2BE value that is the limit of the current protocol.
+static uint16_t CappedAllocRecordCount(size_t alloc_record_count) {
+ if (alloc_record_count > 0xffff) {
+ return 0xffff;
+ }
+ return alloc_record_count;
+}
class AllocRecordStackTraceElement {
public:
@@ -116,9 +124,10 @@
}
void Dbg::TypeCache::Clear() {
- ScopedObjectAccess soa(Thread::Current());
+ JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ Thread* self = Thread::Current();
for (const auto& p : objects_) {
- soa.Vm()->DeleteWeakGlobalRef(soa.Self(), p.second);
+ vm->DeleteWeakGlobalRef(self, p.second);
}
objects_.clear();
}
@@ -131,8 +140,9 @@
return down_cast<mirror::Class*>(Thread::Current()->DecodeJObject(type_));
}
- void SetType(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- type_ = Dbg::GetTypeCache()->Add(t);
+ void SetType(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_,
+ Locks::alloc_tracker_lock_) {
+ type_ = Dbg::type_cache_.Add(t);
}
size_t GetDepth() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -304,7 +314,6 @@
static ObjectRegistry* gRegistry = nullptr;
// Recent allocation tracking.
-Mutex* Dbg::alloc_tracker_lock_ = nullptr;
AllocRecord* Dbg::recent_allocation_records_ = nullptr; // TODO: CircularBuffer<AllocRecord>
size_t Dbg::alloc_record_max_ = 0;
size_t Dbg::alloc_record_head_ = 0;
@@ -312,7 +321,6 @@
Dbg::TypeCache Dbg::type_cache_;
// Deoptimization support.
-Mutex* Dbg::deoptimization_lock_ = nullptr;
std::vector<DeoptimizationRequest> Dbg::deoptimization_requests_;
size_t Dbg::full_deoptimization_event_count_ = 0;
size_t Dbg::delayed_full_undeoptimization_count_ = 0;
@@ -642,8 +650,6 @@
CHECK(gRegistry == nullptr);
gRegistry = new ObjectRegistry;
- alloc_tracker_lock_ = new Mutex("AllocTracker lock");
- deoptimization_lock_ = new Mutex("deoptimization lock", kDeoptimizationLock);
// Init JDWP if the debugger is enabled. This may connect out to a
// debugger, passively listen for a debugger, or block waiting for a
// debugger.
@@ -677,10 +683,6 @@
gJdwpState = nullptr;
delete gRegistry;
gRegistry = nullptr;
- delete alloc_tracker_lock_;
- alloc_tracker_lock_ = nullptr;
- delete deoptimization_lock_;
- deoptimization_lock_ = nullptr;
}
void Dbg::GcDidFinish() {
@@ -747,7 +749,7 @@
}
{
- MutexLock mu(Thread::Current(), *deoptimization_lock_);
+ MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
CHECK_EQ(deoptimization_requests_.size(), 0U);
CHECK_EQ(full_deoptimization_event_count_, 0U);
CHECK_EQ(delayed_full_undeoptimization_count_, 0U);
@@ -792,7 +794,7 @@
// Since we're going to disable deoptimization, we clear the deoptimization requests queue.
// This prevents us from having any pending deoptimization request when the debugger attaches
// to us again while no event has been requested yet.
- MutexLock mu(Thread::Current(), *deoptimization_lock_);
+ MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
deoptimization_requests_.clear();
full_deoptimization_event_count_ = 0U;
delayed_full_undeoptimization_count_ = 0U;
@@ -2922,7 +2924,7 @@
}
void Dbg::DelayFullUndeoptimization() {
- MutexLock mu(Thread::Current(), *deoptimization_lock_);
+ MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
++delayed_full_undeoptimization_count_;
DCHECK_LE(delayed_full_undeoptimization_count_, full_deoptimization_event_count_);
}
@@ -2930,7 +2932,7 @@
void Dbg::ProcessDelayedFullUndeoptimizations() {
// TODO: avoid taking the lock twice (once here and once in ManageDeoptimization).
{
- MutexLock mu(Thread::Current(), *deoptimization_lock_);
+ MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
while (delayed_full_undeoptimization_count_ > 0) {
DeoptimizationRequest req;
req.SetKind(DeoptimizationRequest::kFullUndeoptimization);
@@ -2947,7 +2949,7 @@
// Nothing to do.
return;
}
- MutexLock mu(Thread::Current(), *deoptimization_lock_);
+ MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
RequestDeoptimizationLocked(req);
}
@@ -3025,7 +3027,7 @@
Thread* const self = Thread::Current();
{
// Avoid suspend/resume if there is no pending request.
- MutexLock mu(self, *deoptimization_lock_);
+ MutexLock mu(self, *Locks::deoptimization_lock_);
if (deoptimization_requests_.empty()) {
return;
}
@@ -3037,7 +3039,7 @@
runtime->GetThreadList()->SuspendAll();
const ThreadState old_state = self->SetStateUnsafe(kRunnable);
{
- MutexLock mu(self, *deoptimization_lock_);
+ MutexLock mu(self, *Locks::deoptimization_lock_);
size_t req_index = 0;
for (DeoptimizationRequest& request : deoptimization_requests_) {
VLOG(jdwp) << "Process deoptimization request #" << req_index++;
@@ -4318,30 +4320,40 @@
return kDefaultNumAllocRecords;
}
-void Dbg::SetAllocTrackingEnabled(bool enabled) {
- if (enabled) {
+void Dbg::SetAllocTrackingEnabled(bool enable) {
+ Thread* self = Thread::Current();
+ if (enable) {
{
- MutexLock mu(Thread::Current(), *alloc_tracker_lock_);
- if (recent_allocation_records_ == nullptr) {
- alloc_record_max_ = GetAllocTrackerMax();
- LOG(INFO) << "Enabling alloc tracker (" << alloc_record_max_ << " entries of "
- << kMaxAllocRecordStackDepth << " frames, taking "
- << PrettySize(sizeof(AllocRecord) * alloc_record_max_) << ")";
- alloc_record_head_ = alloc_record_count_ = 0;
- recent_allocation_records_ = new AllocRecord[alloc_record_max_];
- CHECK(recent_allocation_records_ != nullptr);
+ MutexLock mu(self, *Locks::alloc_tracker_lock_);
+ if (recent_allocation_records_ != nullptr) {
+ return; // Already enabled, bail.
}
+ alloc_record_max_ = GetAllocTrackerMax();
+ LOG(INFO) << "Enabling alloc tracker (" << alloc_record_max_ << " entries of "
+ << kMaxAllocRecordStackDepth << " frames, taking "
+ << PrettySize(sizeof(AllocRecord) * alloc_record_max_) << ")";
+ DCHECK_EQ(alloc_record_head_, 0U);
+ DCHECK_EQ(alloc_record_count_, 0U);
+ recent_allocation_records_ = new AllocRecord[alloc_record_max_];
+ CHECK(recent_allocation_records_ != nullptr);
}
Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
} else {
- Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints();
{
- MutexLock mu(Thread::Current(), *alloc_tracker_lock_);
+ ScopedObjectAccess soa(self); // For type_cache_.Clear();
+ MutexLock mu(self, *Locks::alloc_tracker_lock_);
+ if (recent_allocation_records_ == nullptr) {
+ return; // Already disabled, bail.
+ }
LOG(INFO) << "Disabling alloc tracker";
delete[] recent_allocation_records_;
recent_allocation_records_ = nullptr;
+ alloc_record_head_ = 0;
+ alloc_record_count_ = 0;
type_cache_.Clear();
}
+ // If an allocation comes in before we uninstrument, we will safely drop it on the floor.
+ Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints();
}
}
@@ -4381,8 +4393,9 @@
Thread* self = Thread::Current();
CHECK(self != nullptr);
- MutexLock mu(self, *alloc_tracker_lock_);
+ MutexLock mu(self, *Locks::alloc_tracker_lock_);
if (recent_allocation_records_ == nullptr) {
+ // In the process of shutting down recording, bail.
return;
}
@@ -4408,12 +4421,12 @@
// Returns the index of the head element.
//
-// We point at the most-recently-written record, so if gAllocRecordCount is 1
+// We point at the most-recently-written record, so if alloc_record_count_ is 1
// we want to use the current element. Take "head+1" and subtract count
// from it.
//
// We need to handle underflow in our circular buffer, so we add
-// gAllocRecordMax and then mask it back down.
+// alloc_record_max_ and then mask it back down.
size_t Dbg::HeadIndex() {
return (Dbg::alloc_record_head_ + 1 + Dbg::alloc_record_max_ - Dbg::alloc_record_count_) &
(Dbg::alloc_record_max_ - 1);
@@ -4421,7 +4434,7 @@
void Dbg::DumpRecentAllocations() {
ScopedObjectAccess soa(Thread::Current());
- MutexLock mu(soa.Self(), *alloc_tracker_lock_);
+ MutexLock mu(soa.Self(), *Locks::alloc_tracker_lock_);
if (recent_allocation_records_ == nullptr) {
LOG(INFO) << "Not recording tracked allocations";
return;
@@ -4430,7 +4443,8 @@
// "i" is the head of the list. We want to start at the end of the
// list and move forward to the tail.
size_t i = HeadIndex();
- size_t count = alloc_record_count_;
+ const uint16_t capped_count = CappedAllocRecordCount(Dbg::alloc_record_count_);
+ uint16_t count = capped_count;
LOG(INFO) << "Tracked allocations, (head=" << alloc_record_head_ << " count=" << count << ")";
while (count--) {
@@ -4534,7 +4548,7 @@
* followed by UTF-16 data.
*
* We send up 16-bit unsigned indexes into string tables. In theory there
- * can be (kMaxAllocRecordStackDepth * gAllocRecordMax) unique strings in
+ * can be (kMaxAllocRecordStackDepth * alloc_record_max_) unique strings in
* each table, but in practice there should be far fewer.
*
* The chief reason for using a string table here is to keep the size of
@@ -4554,7 +4568,7 @@
Thread* self = Thread::Current();
std::vector<uint8_t> bytes;
{
- MutexLock mu(self, *alloc_tracker_lock_);
+ MutexLock mu(self, *Locks::alloc_tracker_lock_);
//
// Part 1: generate string tables.
//
@@ -4562,8 +4576,9 @@
StringTable method_names;
StringTable filenames;
- int count = alloc_record_count_;
- int idx = HeadIndex();
+ const uint16_t capped_count = CappedAllocRecordCount(Dbg::alloc_record_count_);
+ uint16_t count = capped_count;
+ size_t idx = HeadIndex();
while (count--) {
AllocRecord* record = &recent_allocation_records_[idx];
std::string temp;
@@ -4580,7 +4595,7 @@
idx = (idx + 1) & (alloc_record_max_ - 1);
}
- LOG(INFO) << "allocation records: " << alloc_record_count_;
+ LOG(INFO) << "allocation records: " << capped_count;
//
// Part 2: Generate the output and store it in the buffer.
@@ -4601,7 +4616,7 @@
// (2b) number of class name strings
// (2b) number of method name strings
// (2b) number of source file name strings
- JDWP::Append2BE(bytes, alloc_record_count_);
+ JDWP::Append2BE(bytes, capped_count);
size_t string_table_offset = bytes.size();
JDWP::Append4BE(bytes, 0); // We'll patch this later...
JDWP::Append2BE(bytes, class_names.Size());
@@ -4610,7 +4625,7 @@
idx = HeadIndex();
std::string temp;
- for (count = alloc_record_count_; count != 0; --count) {
+ for (count = capped_count; count != 0; --count) {
// For each entry:
// (4b) total allocation size
// (2b) thread id
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 52ae7a9..3e16288 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -192,9 +192,11 @@
class TypeCache {
public:
// Returns a weak global for the input type. Deduplicates.
- jobject Add(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ jobject Add(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_,
+ Locks::alloc_tracker_lock_);
// Clears the type cache and deletes all the weak global refs.
- void Clear();
+ void Clear() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_,
+ Locks::alloc_tracker_lock_);
private:
std::multimap<int32_t, jobject> objects_;
@@ -221,8 +223,8 @@
*/
static void Connected();
static void GoActive()
- LOCKS_EXCLUDED(Locks::breakpoint_lock_, deoptimization_lock_, Locks::mutator_lock_);
- static void Disconnected() LOCKS_EXCLUDED(deoptimization_lock_, Locks::mutator_lock_);
+ LOCKS_EXCLUDED(Locks::breakpoint_lock_, Locks::deoptimization_lock_, Locks::mutator_lock_);
+ static void Disconnected() LOCKS_EXCLUDED(Locks::deoptimization_lock_, Locks::mutator_lock_);
static void Disposed();
// Returns true if we're actually debugging with a real debugger, false if it's
@@ -493,20 +495,20 @@
// Records deoptimization request in the queue.
static void RequestDeoptimization(const DeoptimizationRequest& req)
- LOCKS_EXCLUDED(deoptimization_lock_)
+ LOCKS_EXCLUDED(Locks::deoptimization_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Support delayed full undeoptimization requests. This is currently only used for single-step
// events.
- static void DelayFullUndeoptimization() LOCKS_EXCLUDED(deoptimization_lock_);
+ static void DelayFullUndeoptimization() LOCKS_EXCLUDED(Locks::deoptimization_lock_);
static void ProcessDelayedFullUndeoptimizations()
- LOCKS_EXCLUDED(deoptimization_lock_)
+ LOCKS_EXCLUDED(Locks::deoptimization_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Manage deoptimization after updating JDWP events list. Suspends all threads, processes each
// request and finally resumes all threads.
static void ManageDeoptimization()
- LOCKS_EXCLUDED(deoptimization_lock_)
+ LOCKS_EXCLUDED(Locks::deoptimization_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Breakpoints.
@@ -560,17 +562,17 @@
* Recent allocation tracking support.
*/
static void RecordAllocation(mirror::Class* type, size_t byte_count)
- LOCKS_EXCLUDED(alloc_tracker_lock_)
+ LOCKS_EXCLUDED(Locks::alloc_tracker_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void SetAllocTrackingEnabled(bool enabled) LOCKS_EXCLUDED(alloc_tracker_lock_);
+ static void SetAllocTrackingEnabled(bool enabled) LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
static bool IsAllocTrackingEnabled() {
return recent_allocation_records_ != nullptr;
}
static jbyteArray GetRecentAllocations()
- LOCKS_EXCLUDED(alloc_tracker_lock_)
+ LOCKS_EXCLUDED(Locks::alloc_tracker_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static size_t HeadIndex() EXCLUSIVE_LOCKS_REQUIRED(alloc_tracker_lock_);
- static void DumpRecentAllocations() LOCKS_EXCLUDED(alloc_tracker_lock_);
+ static size_t HeadIndex() EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
+ static void DumpRecentAllocations() LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
enum HpifWhen {
HPIF_WHEN_NEVER = 0,
@@ -596,10 +598,6 @@
static void DdmSendHeapSegments(bool native)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static TypeCache* GetTypeCache() {
- return &type_cache_;
- }
-
private:
static void DdmBroadcast(bool connect) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void PostThreadStartOrStop(Thread*, uint32_t)
@@ -617,52 +615,47 @@
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
static void RequestDeoptimizationLocked(const DeoptimizationRequest& req)
- EXCLUSIVE_LOCKS_REQUIRED(deoptimization_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::deoptimization_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static Mutex* alloc_tracker_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-
- static AllocRecord* recent_allocation_records_ PT_GUARDED_BY(alloc_tracker_lock_);
- static size_t alloc_record_max_ GUARDED_BY(alloc_tracker_lock_);
- static size_t alloc_record_head_ GUARDED_BY(alloc_tracker_lock_);
- static size_t alloc_record_count_ GUARDED_BY(alloc_tracker_lock_);
-
- // Guards deoptimization requests.
- // TODO rename to instrumentation_update_lock.
- static Mutex* deoptimization_lock_ ACQUIRED_AFTER(Locks::breakpoint_lock_);
+ static AllocRecord* recent_allocation_records_ PT_GUARDED_BY(Locks::alloc_tracker_lock_);
+ static size_t alloc_record_max_ GUARDED_BY(Locks::alloc_tracker_lock_);
+ static size_t alloc_record_head_ GUARDED_BY(Locks::alloc_tracker_lock_);
+ static size_t alloc_record_count_ GUARDED_BY(Locks::alloc_tracker_lock_);
// Deoptimization requests to be processed each time the event list is updated. This is used when
// registering and unregistering events so we do not deoptimize while holding the event list
// lock.
// TODO rename to instrumentation_requests.
- static std::vector<DeoptimizationRequest> deoptimization_requests_ GUARDED_BY(deoptimization_lock_);
+ static std::vector<DeoptimizationRequest> deoptimization_requests_ GUARDED_BY(Locks::deoptimization_lock_);
// Count the number of events requiring full deoptimization. When the counter is > 0, everything
// is deoptimized, otherwise everything is undeoptimized.
// Note: we fully deoptimize on the first event only (when the counter is set to 1). We fully
// undeoptimize when the last event is unregistered (when the counter is set to 0).
- static size_t full_deoptimization_event_count_ GUARDED_BY(deoptimization_lock_);
+ static size_t full_deoptimization_event_count_ GUARDED_BY(Locks::deoptimization_lock_);
// Count the number of full undeoptimization requests delayed to next resume or end of debug
// session.
- static size_t delayed_full_undeoptimization_count_ GUARDED_BY(deoptimization_lock_);
+ static size_t delayed_full_undeoptimization_count_ GUARDED_BY(Locks::deoptimization_lock_);
static size_t* GetReferenceCounterForEvent(uint32_t instrumentation_event);
// Weak global type cache, TODO improve this.
- static TypeCache type_cache_;
+ static TypeCache type_cache_ GUARDED_BY(Locks::alloc_tracker_lock_);
// Instrumentation event reference counters.
// TODO we could use an array instead of having all these dedicated counters. Instrumentation
// events are bits of a mask so we could convert them to array index.
- static size_t dex_pc_change_event_ref_count_ GUARDED_BY(deoptimization_lock_);
- static size_t method_enter_event_ref_count_ GUARDED_BY(deoptimization_lock_);
- static size_t method_exit_event_ref_count_ GUARDED_BY(deoptimization_lock_);
- static size_t field_read_event_ref_count_ GUARDED_BY(deoptimization_lock_);
- static size_t field_write_event_ref_count_ GUARDED_BY(deoptimization_lock_);
- static size_t exception_catch_event_ref_count_ GUARDED_BY(deoptimization_lock_);
+ static size_t dex_pc_change_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
+ static size_t method_enter_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
+ static size_t method_exit_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
+ static size_t field_read_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
+ static size_t field_write_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
+ static size_t exception_catch_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
static uint32_t instrumentation_events_ GUARDED_BY(Locks::mutator_lock_);
+ friend class AllocRecord; // For type_cache_ with proper annotalysis.
DISALLOW_COPY_AND_ASSIGN(Dbg);
};
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index e1c532e..d834d4d 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -114,11 +114,6 @@
// We don't fail here because SetStackEndForStackOverflow will print better diagnostics.
}
- if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
- // Remove extra entry pushed onto second stack during method tracing.
- Runtime::Current()->GetInstrumentation()->PopMethodForUnwind(self, false);
- }
-
self->SetStackEndForStackOverflow(); // Allow space on the stack for constructor to execute.
JNIEnvExt* env = self->GetJniEnv();
std::string msg("stack size ");
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 25f87c5..47696f9 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -151,12 +151,73 @@
// We hit a signal we didn't handle. This might be something for which
// we can give more information about so call all registered handlers to see
// if it is.
- for (const auto& handler : other_handlers_) {
- if (handler->Action(sig, info, context)) {
- return;
+
+ Thread* self = Thread::Current();
+
+ // Now set up the nested signal handler.
+
+ // Shutdown the fault manager so that it will remove the signal chain for
+ // SIGSEGV and we call the real sigaction.
+ fault_manager.Shutdown();
+
+ // The action for SIGSEGV should be the default handler now.
+
+ // Unblock the signals we allow so that they can be delivered in the signal handler.
+ sigset_t sigset;
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGSEGV);
+ sigaddset(&sigset, SIGABRT);
+ pthread_sigmask(SIG_UNBLOCK, &sigset, nullptr);
+
+ // If we get a signal in this code we want to invoke our nested signal
+ // handler.
+ struct sigaction action, oldsegvaction, oldabortaction;
+ action.sa_sigaction = art_nested_signal_handler;
+
+ // Explicitly mask out SIGSEGV and SIGABRT from the nested signal handler. This
+ // should be the default but we definitely don't want these happening in our
+ // nested signal handler.
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, SIGSEGV);
+ sigaddset(&action.sa_mask, SIGABRT);
+
+ action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+#if !defined(__APPLE__) && !defined(__mips__)
+ action.sa_restorer = nullptr;
+#endif
+
+ // Catch SIGSEGV and SIGABRT to invoke our nested handler
+ int e1 = sigaction(SIGSEGV, &action, &oldsegvaction);
+ int e2 = sigaction(SIGABRT, &action, &oldabortaction);
+ if (e1 != 0 || e2 != 0) {
+ LOG(ERROR) << "Unable to set up nested signal handler";
+ } else {
+ // Save the current state and call the handlers. If anything causes a signal
+ // our nested signal handler will be invoked and this will longjmp to the saved
+ // state.
+ if (setjmp(*self->GetNestedSignalState()) == 0) {
+ for (const auto& handler : other_handlers_) {
+ if (handler->Action(sig, info, context)) {
+ // Restore the signal handlers, reinit the fault manager and return. Signal was
+ // handled.
+ sigaction(SIGSEGV, &oldsegvaction, nullptr);
+ sigaction(SIGABRT, &oldabortaction, nullptr);
+ fault_manager.Init();
+ return;
+ }
+ }
+ } else {
+ LOG(ERROR) << "Nested signal detected - original signal being reported";
}
+
+ // Restore the signal handlers.
+ sigaction(SIGSEGV, &oldsegvaction, nullptr);
+ sigaction(SIGABRT, &oldabortaction, nullptr);
}
+ // Now put the fault manager back in place.
+ fault_manager.Init();
+
// Set a breakpoint in this function to catch unhandled signals.
art_sigsegv_fault();
@@ -311,75 +372,18 @@
uintptr_t sp = 0;
Thread* self = Thread::Current();
- // Shutdown the fault manager so that it will remove the signal chain for
- // SIGSEGV and we call the real sigaction.
- fault_manager.Shutdown();
-
- // The action for SIGSEGV should be the default handler now.
-
- // Unblock the signals we allow so that they can be delivered in the signal handler.
- sigset_t sigset;
- sigemptyset(&sigset);
- sigaddset(&sigset, SIGSEGV);
- sigaddset(&sigset, SIGABRT);
- pthread_sigmask(SIG_UNBLOCK, &sigset, nullptr);
-
- // If we get a signal in this code we want to invoke our nested signal
- // handler.
- struct sigaction action, oldsegvaction, oldabortaction;
- action.sa_sigaction = art_nested_signal_handler;
-
- // Explictly mask out SIGSEGV and SIGABRT from the nested signal handler. This
- // should be the default but we definitely don't want these happening in our
- // nested signal handler.
- sigemptyset(&action.sa_mask);
- sigaddset(&action.sa_mask, SIGSEGV);
- sigaddset(&action.sa_mask, SIGABRT);
-
- action.sa_flags = SA_SIGINFO | SA_ONSTACK;
-#if !defined(__APPLE__) && !defined(__mips__)
- action.sa_restorer = nullptr;
-#endif
-
- // Catch SIGSEGV and SIGABRT to invoke our nested handler
- int e1 = sigaction(SIGSEGV, &action, &oldsegvaction);
- int e2 = sigaction(SIGABRT, &action, &oldabortaction);
- if (e1 != 0 || e2 != 0) {
- LOG(ERROR) << "Unable to register nested signal handler - no stack trace possible";
- // If sigaction failed we have a serious problem. We cannot catch
- // any failures in the stack tracer and it's likely to occur since
- // the program state is bad. Therefore we don't even try to give
- // a stack trace.
- } else {
- // Save the current state and try to dump the stack. If this causes a signal
- // our nested signal handler will be invoked and this will longjmp to the saved
- // state.
- if (setjmp(*self->GetNestedSignalState()) == 0) {
- manager_->GetMethodAndReturnPcAndSp(siginfo, context, &method, &return_pc, &sp);
- // Inside of generated code, sp[0] is the method, so sp is the frame.
- StackReference<mirror::ArtMethod>* frame =
- reinterpret_cast<StackReference<mirror::ArtMethod>*>(sp);
- self->SetTopOfStack(frame, 0); // Since we don't necessarily have a dex pc, pass in 0.
+ manager_->GetMethodAndReturnPcAndSp(siginfo, context, &method, &return_pc, &sp);
+ // Inside of generated code, sp[0] is the method, so sp is the frame.
+ StackReference<mirror::ArtMethod>* frame =
+ reinterpret_cast<StackReference<mirror::ArtMethod>*>(sp);
+ self->SetTopOfStack(frame, 0); // Since we don't necessarily have a dex pc, pass in 0.
#ifdef TEST_NESTED_SIGNAL
- // To test the nested signal handler we raise a signal here. This will cause the
- // nested signal handler to be called and perform a longjmp back to the setjmp
- // above.
- abort();
+ // To test the nested signal handler we raise a signal here. This will cause the
+ // nested signal handler to be called and perform a longjmp back to the setjmp
+ // above.
+ abort();
#endif
- self->DumpJavaStack(LOG(ERROR));
- } else {
- LOG(ERROR) << "Stack trace aborted due to nested signal - original signal being reported";
- }
-
- // Restore the signal handlers.
- sigaction(SIGSEGV, &oldsegvaction, nullptr);
- sigaction(SIGABRT, &oldabortaction, nullptr);
- }
-
- // Now put the fault manager back in place.
- fault_manager.Init();
-
- // And we're done.
+ self->DumpJavaStack(LOG(ERROR));
}
return false; // Return false since we want to propagate the fault to the main signal handler.
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 5d0d8d2..494781a 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -120,7 +120,7 @@
desired_collector_type_(foreground_collector_type_),
heap_trim_request_lock_(nullptr),
last_trim_time_(0),
- heap_transition_or_trim_target_time_(0),
+ last_heap_transition_time_(0),
heap_trim_request_pending_(false),
parallel_gc_threads_(parallel_gc_threads),
conc_gc_threads_(conc_gc_threads),
@@ -916,35 +916,6 @@
}
void Heap::DoPendingTransitionOrTrim() {
- Thread* self = Thread::Current();
- CollectorType desired_collector_type;
- // Wait until we reach the desired transition time.
- while (true) {
- uint64_t wait_time;
- {
- MutexLock mu(self, *heap_trim_request_lock_);
- desired_collector_type = desired_collector_type_;
- uint64_t current_time = NanoTime();
- if (current_time >= heap_transition_or_trim_target_time_) {
- break;
- }
- wait_time = heap_transition_or_trim_target_time_ - current_time;
- }
- ScopedThreadStateChange tsc(self, kSleeping);
- usleep(wait_time / 1000); // Usleep takes microseconds.
- }
- // Launch homogeneous space compaction if it is desired.
- if (desired_collector_type == kCollectorTypeHomogeneousSpaceCompact) {
- if (!CareAboutPauseTimes()) {
- PerformHomogeneousSpaceCompact();
- }
- // No need to Trim(). Homogeneous space compaction may free more virtual and physical memory.
- desired_collector_type = collector_type_;
- return;
- }
- // Transition the collector if the desired collector type is not the same as the current
- // collector type.
- TransitionCollector(desired_collector_type);
if (!CareAboutPauseTimes()) {
// Deflate the monitors, this can cause a pause but shouldn't matter since we don't care
// about pauses.
@@ -956,7 +927,23 @@
<< PrettyDuration(NanoTime() - start_time);
runtime->GetThreadList()->ResumeAll();
}
- // Do a heap trim if it is needed.
+ if (NanoTime() - last_heap_transition_time_ > kCollectorTransitionWait) {
+ // Launch homogeneous space compaction if it is desired.
+ if (desired_collector_type_ == kCollectorTypeHomogeneousSpaceCompact) {
+ if (!CareAboutPauseTimes()) {
+ PerformHomogeneousSpaceCompact();
+ last_heap_transition_time_ = NanoTime();
+ }
+ desired_collector_type_ = collector_type_;
+ } else {
+ // Transition the collector if the desired collector type is not the same as the current
+ // collector type.
+ TransitionCollector(desired_collector_type_);
+ last_heap_transition_time_ = NanoTime();
+ }
+ }
+ // Do a heap trim if it is needed. This is good to do even with hspace compaction since it may
+ // trim the native heap and dlmalloc spaces.
Trim();
}
@@ -2990,8 +2977,6 @@
if (desired_collector_type_ == desired_collector_type) {
return;
}
- heap_transition_or_trim_target_time_ =
- std::max(heap_transition_or_trim_target_time_, NanoTime() + delta_time);
desired_collector_type_ = desired_collector_type;
}
SignalHeapTrimDaemon(self);
@@ -3027,10 +3012,6 @@
return;
}
heap_trim_request_pending_ = true;
- uint64_t current_time = NanoTime();
- if (heap_transition_or_trim_target_time_ < current_time) {
- heap_transition_or_trim_target_time_ = current_time + kHeapTrimWait;
- }
}
// Notify the daemon thread which will actually do the heap trim.
SignalHeapTrimDaemon(self);
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 9742277..3bfa748 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -830,8 +830,8 @@
Mutex* heap_trim_request_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
// When we want to perform the next heap trim (nano seconds).
uint64_t last_trim_time_ GUARDED_BY(heap_trim_request_lock_);
- // When we want to perform the next heap transition (nano seconds) or heap trim.
- uint64_t heap_transition_or_trim_target_time_ GUARDED_BY(heap_trim_request_lock_);
+ // When we last performed a heap transition or hspace compact.
+ uint64_t last_heap_transition_time_;
// If we have a heap trim request pending.
bool heap_trim_request_pending_ GUARDED_BY(heap_trim_request_lock_);
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index df6055d..c3c8c25 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -17,9 +17,11 @@
#include <stdlib.h>
#include "debugger.h"
+#include "instruction_set.h"
#include "java_vm_ext.h"
#include "jni_internal.h"
#include "JNIHelp.h"
+#include "ScopedUtfChars.h"
#include "thread-inl.h"
#if defined(HAVE_PRCTL)
@@ -102,17 +104,27 @@
return reinterpret_cast<jlong>(self);
}
-static void ZygoteHooks_nativePostForkChild(JNIEnv* env, jclass, jlong token, jint debug_flags) {
+static void ZygoteHooks_nativePostForkChild(JNIEnv* env, jclass, jlong token, jint debug_flags,
+ jstring instruction_set) {
Thread* thread = reinterpret_cast<Thread*>(token);
// Our system thread ID, etc, has changed so reset Thread state.
thread->InitAfterFork();
EnableDebugFeatures(debug_flags);
- Runtime::Current()->DidForkFromZygote();
+
+ Runtime::NativeBridgeAction action = Runtime::NativeBridgeAction::kUnload;
+ if (instruction_set != nullptr) {
+ ScopedUtfChars isa_string(env, instruction_set);
+ InstructionSet isa = GetInstructionSetFromString(isa_string.c_str());
+ if (isa != kNone && isa != kRuntimeISA) {
+ action = Runtime::NativeBridgeAction::kInitialize;
+ }
+ }
+ Runtime::Current()->DidForkFromZygote(action);
}
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(ZygoteHooks, nativePreFork, "()J"),
- NATIVE_METHOD(ZygoteHooks, nativePostForkChild, "(JI)V"),
+ NATIVE_METHOD(ZygoteHooks, nativePostForkChild, "(JILjava/lang/String;)V"),
};
void register_dalvik_system_ZygoteHooks(JNIEnv* env) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 5adb5d0..89ad505 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -428,7 +428,7 @@
return false;
}
} else {
- DidForkFromZygote();
+ DidForkFromZygote(NativeBridgeAction::kInitialize);
}
StartDaemonThreads();
@@ -506,9 +506,19 @@
#endif
}
-void Runtime::DidForkFromZygote() {
+void Runtime::DidForkFromZygote(NativeBridgeAction action) {
is_zygote_ = false;
+ switch (action) {
+ case NativeBridgeAction::kUnload:
+ android::UnloadNativeBridge();
+ break;
+
+ case NativeBridgeAction::kInitialize:
+ android::InitializeNativeBridge();
+ break;
+ }
+
// Create the thread pool.
heap_->CreateThreadPool();
@@ -833,8 +843,34 @@
self->ClearException();
// Look for a native bridge.
+ //
+ // The intended flow here is, in the case of a running system:
+ //
+ // Runtime::Init() (zygote):
+ // LoadNativeBridge -> dlopen from cmd line parameter.
+ // |
+ // V
+ // Runtime::Start() (zygote):
+ // No-op wrt native bridge.
+ // |
+ // | start app
+ // V
+ // DidForkFromZygote(action)
+ // action = kUnload -> dlclose native bridge.
+ // action = kInitialize -> initialize library
+ //
+ //
+ // The intended flow here is, in the case of a simple dalvikvm call:
+ //
+ // Runtime::Init():
+ // LoadNativeBridge -> dlopen from cmd line parameter.
+ // |
+ // V
+ // Runtime::Start():
+ // DidForkFromZygote(kInitialize) -> try to initialize any native bridge given.
+ // No-op wrt native bridge.
native_bridge_library_filename_ = options->native_bridge_library_filename_;
- android::SetupNativeBridge(native_bridge_library_filename_.c_str(), &native_bridge_art_callbacks_);
+ android::LoadNativeBridge(native_bridge_library_filename_.c_str(), &native_bridge_art_callbacks_);
VLOG(startup) << "Runtime::Setup native bridge library: "
<< (native_bridge_library_filename_.empty() ?
"(empty)" : native_bridge_library_filename_);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 632806f..a0993ca 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -393,9 +393,13 @@
void SetStatsEnabled(bool new_state);
+ enum class NativeBridgeAction { // private
+ kUnload,
+ kInitialize
+ };
void PreZygoteFork();
bool InitZygote();
- void DidForkFromZygote();
+ void DidForkFromZygote(NativeBridgeAction action);
const instrumentation::Instrumentation* GetInstrumentation() const {
return &instrumentation_;
diff --git a/test/003-omnibus-opcodes/expected.txt b/test/003-omnibus-opcodes/expected.txt
index a62c498..b591a7a 100644
--- a/test/003-omnibus-opcodes/expected.txt
+++ b/test/003-omnibus-opcodes/expected.txt
@@ -72,4 +72,5 @@
UnresTest2...
UnresTest2 done
InternedString.run
+null
Done!
diff --git a/test/003-omnibus-opcodes/src/GenSelect.java b/test/003-omnibus-opcodes/src/GenSelect.java
new file mode 100644
index 0000000..7e3481b
--- /dev/null
+++ b/test/003-omnibus-opcodes/src/GenSelect.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class GenSelect {
+ public static String test(boolean b) {
+ String str1 = null;
+ String str2 = null;
+ String res = b ? str1 : str2;
+ return res;
+ }
+
+ public static void run() {
+ System.out.println(test(true));
+ }
+}
diff --git a/test/003-omnibus-opcodes/src/Main.java b/test/003-omnibus-opcodes/src/Main.java
index 25050df..a30ec15 100644
--- a/test/003-omnibus-opcodes/src/Main.java
+++ b/test/003-omnibus-opcodes/src/Main.java
@@ -70,6 +70,7 @@
th.printStackTrace();
}
InternedString.run();
+ GenSelect.run();
}
public static void assertTrue(boolean condition) {
diff --git a/test/036-finalizer/expected.txt b/test/036-finalizer/expected.txt
index a2a74fc..36fa5f8 100644
--- a/test/036-finalizer/expected.txt
+++ b/test/036-finalizer/expected.txt
@@ -11,3 +11,4 @@
sleep
reborn: [FinalizerTest message=nothing, finalized=false]
wimp: null
+Finalized 1024 / 1024
diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java
index 328425f..390472d 100644
--- a/test/036-finalizer/src/Main.java
+++ b/test/036-finalizer/src/Main.java
@@ -120,6 +120,39 @@
System.out.println("reborn: " + FinalizerTest.mReborn);
System.out.println("wimp: " + wimpString(wimp));
+ // Test runFinalization with multiple objects.
+ runFinalizationTest();
+ }
+
+ static class FinalizeCounter {
+ private static Object finalizeLock = new Object();
+ private static volatile int finalizeCount = 0;
+ private int index;
+ static int getCount() {
+ return finalizeCount;
+ }
+ FinalizeCounter(int index) {
+ this.index = index;
+ }
+ protected void finalize() {
+ synchronized(finalizeLock) {
+ ++finalizeCount;
+ }
+ }
+ }
+
+ private static void runFinalizationTest() {
+ int count = 1024;
+ Object[] objs = new Object[count];
+ for (int i = 0; i < count; ++i) {
+ objs[i] = new FinalizeCounter(i);
+ }
+ for (int i = 0; i < count; ++i) {
+ objs[i] = null;
+ }
+ System.gc();
+ System.runFinalization();
+ System.out.println("Finalized " + FinalizeCounter.getCount() + " / " + count);
}
public static class FinalizerTest {
diff --git a/test/098-ddmc/expected.txt b/test/098-ddmc/expected.txt
new file mode 100644
index 0000000..f8cda4c
--- /dev/null
+++ b/test/098-ddmc/expected.txt
@@ -0,0 +1,23 @@
+Confirm empty
+empty=Allocations[message header len: 15 entry header len: 9 stack frame len: 8 number of entries: 0 offset to string table from start of message: 15 number of class name strings: 0 number of method name strings: 0 number of source file name strings: 0]
+Confirm enable
+status=false
+status=true
+Capture some allocations (note just this causes allocations)
+before > 0=true
+Confirm when we overflow, we don't roll over to zero. b/17392248
+before < overflowAllocations=true
+after > before=true
+after.numberOfEntries=65535
+Disable and confirm back to empty
+status=false
+reset=Allocations[message header len: 15 entry header len: 9 stack frame len: 8 number of entries: 0 offset to string table from start of message: 15 number of class name strings: 0 number of method name strings: 0 number of source file name strings: 0]
+Confirm we can disable twice in a row
+status=false
+status=false
+Confirm we can reenable twice in a row without losing allocations
+status=true
+status=true
+second > first =true
+Goodbye
+goodbye=Allocations[message header len: 15 entry header len: 9 stack frame len: 8 number of entries: 0 offset to string table from start of message: 15 number of class name strings: 0 number of method name strings: 0 number of source file name strings: 0]
diff --git a/test/098-ddmc/info.txt b/test/098-ddmc/info.txt
new file mode 100644
index 0000000..39d26db
--- /dev/null
+++ b/test/098-ddmc/info.txt
@@ -0,0 +1 @@
+Tests of private org.apache.harmony.dalvik.ddmc.* APIs used for ddms support.
diff --git a/test/098-ddmc/src/Main.java b/test/098-ddmc/src/Main.java
new file mode 100644
index 0000000..962bd7f
--- /dev/null
+++ b/test/098-ddmc/src/Main.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ String name = System.getProperty("java.vm.name");
+ if (!"Dalvik".equals(name)) {
+ System.out.println("This test is not supported on " + name);
+ return;
+ }
+ testRecentAllocationTracking();
+ }
+
+ private static void testRecentAllocationTracking() throws Exception {
+ System.out.println("Confirm empty");
+ Allocations empty = new Allocations(DdmVmInternal.getRecentAllocations());
+ System.out.println("empty=" + empty);
+
+ System.out.println("Confirm enable");
+ System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+ DdmVmInternal.enableRecentAllocations(true);
+ System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+
+ System.out.println("Capture some allocations (note just this causes allocations)");
+ Allocations before = new Allocations(DdmVmInternal.getRecentAllocations());
+ System.out.println("before > 0=" + (before.numberOfEntries > 0));
+
+ System.out.println("Confirm when we overflow, we don't roll over to zero. b/17392248");
+ final int overflowAllocations = 64 * 1024; // Won't fit in unsigned 16-bit value.
+ for (int i = 0; i < overflowAllocations; i++) {
+ new String("fnord");
+ }
+ Allocations after = new Allocations(DdmVmInternal.getRecentAllocations());
+ System.out.println("before < overflowAllocations=" + (before.numberOfEntries < overflowAllocations));
+ System.out.println("after > before=" + (after.numberOfEntries > before.numberOfEntries));
+ System.out.println("after.numberOfEntries=" + after.numberOfEntries);
+
+ System.out.println("Disable and confirm back to empty");
+ DdmVmInternal.enableRecentAllocations(false);
+ System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+ Allocations reset = new Allocations(DdmVmInternal.getRecentAllocations());
+ System.out.println("reset=" + reset);
+
+ System.out.println("Confirm we can disable twice in a row");
+ DdmVmInternal.enableRecentAllocations(false);
+ System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+ DdmVmInternal.enableRecentAllocations(false);
+ System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+
+ System.out.println("Confirm we can reenable twice in a row without losing allocations");
+ DdmVmInternal.enableRecentAllocations(true);
+ System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+ for (int i = 0; i < 16 * 1024; i++) {
+ new String("fnord");
+ }
+ Allocations first = new Allocations(DdmVmInternal.getRecentAllocations());
+ DdmVmInternal.enableRecentAllocations(true);
+ System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+ Allocations second = new Allocations(DdmVmInternal.getRecentAllocations());
+ System.out.println("second > first =" + (second.numberOfEntries > first.numberOfEntries));
+
+ System.out.println("Goodbye");
+ DdmVmInternal.enableRecentAllocations(false);
+ Allocations goodbye = new Allocations(DdmVmInternal.getRecentAllocations());
+ System.out.println("goodbye=" + goodbye);
+ }
+
+ private static class Allocations {
+ final int messageHeaderLen;
+ final int entryHeaderLen;
+ final int stackFrameLen;
+ final int numberOfEntries;
+ final int offsetToStringTableFromStartOfMessage;
+ final int numberOfClassNameStrings;
+ final int numberOfMethodNameStrings;
+ final int numberOfSourceFileNameStrings;
+
+ Allocations(byte[] allocations) {
+ ByteBuffer b = ByteBuffer.wrap(allocations);
+ messageHeaderLen = b.get() & 0xff;
+ if (messageHeaderLen != 15) {
+ throw new IllegalArgumentException("Unexpected messageHeaderLen " + messageHeaderLen);
+ }
+ entryHeaderLen = b.get() & 0xff;
+ if (entryHeaderLen != 9) {
+ throw new IllegalArgumentException("Unexpected entryHeaderLen " + entryHeaderLen);
+ }
+ stackFrameLen = b.get() & 0xff;
+ if (stackFrameLen != 8) {
+ throw new IllegalArgumentException("Unexpected messageHeaderLen " + stackFrameLen);
+ }
+ numberOfEntries = b.getShort() & 0xffff;
+ offsetToStringTableFromStartOfMessage = b.getInt();
+ numberOfClassNameStrings = b.getShort() & 0xffff;
+ numberOfMethodNameStrings = b.getShort() & 0xffff;
+ numberOfSourceFileNameStrings = b.getShort() & 0xffff;
+ }
+
+ public String toString() {
+ return ("Allocations[message header len: " + messageHeaderLen +
+ " entry header len: " + entryHeaderLen +
+ " stack frame len: " + stackFrameLen +
+ " number of entries: " + numberOfEntries +
+ " offset to string table from start of message: " + offsetToStringTableFromStartOfMessage +
+ " number of class name strings: " + numberOfClassNameStrings +
+ " number of method name strings: " + numberOfMethodNameStrings +
+ " number of source file name strings: " + numberOfSourceFileNameStrings +
+ "]");
+ }
+ }
+
+ private static class DdmVmInternal {
+ private static final Method enableRecentAllocationsMethod;
+ private static final Method getRecentAllocationStatusMethod;
+ private static final Method getRecentAllocationsMethod;
+ static {
+ try {
+ Class c = Class.forName("org.apache.harmony.dalvik.ddmc.DdmVmInternal");
+ enableRecentAllocationsMethod = c.getDeclaredMethod("enableRecentAllocations",
+ Boolean.TYPE);
+ getRecentAllocationStatusMethod = c.getDeclaredMethod("getRecentAllocationStatus");
+ getRecentAllocationsMethod = c.getDeclaredMethod("getRecentAllocations");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void enableRecentAllocations(boolean enable) throws Exception {
+ enableRecentAllocationsMethod.invoke(null, enable);
+ }
+ public static boolean getRecentAllocationStatus() throws Exception {
+ return (boolean) getRecentAllocationStatusMethod.invoke(null);
+ }
+ public static byte[] getRecentAllocations() throws Exception {
+ return (byte[]) getRecentAllocationsMethod.invoke(null);
+ }
+ }
+}
diff --git a/test/115-native-bridge/expected.txt b/test/115-native-bridge/expected.txt
index 5b41606..808d968 100644
--- a/test/115-native-bridge/expected.txt
+++ b/test/115-native-bridge/expected.txt
@@ -1,5 +1,5 @@
-Ready for native bridge tests.
Native bridge initialized.
+Ready for native bridge tests.
Checking for support.
Getting trampoline for JNI_OnLoad with shorty (null).
Test ART callbacks: all JNI function number is 9.
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 7da57dd..773a950 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -193,21 +193,6 @@
TEST_ART_BROKEN_NO_RELOCATE_TESTS :=
-# Tests that are broken with tracing.
-TEST_ART_BROKEN_TRACE_RUN_TESTS := \
- 004-SignalTest \
- 018-stack-overflow \
- 097-duplicate-method \
- 107-int-math2
-
-ifneq (,$(filter trace,$(TRACE_TYPES)))
- ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \
- $(COMPILER_TYPES),$(RELOCATE_TYPES),trace,$(GC_TYPES),$(JNI_TYPES), \
- $(IMAGE_TYPES), $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(ALL_ADDRESS_SIZES))
-endif
-
-TEST_ART_BROKEN_TRACE_RUN_TESTS :=
-
# Tests that are broken with GC stress.
TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \
004-SignalTest
diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar
index 54a0865..4485590 100755
--- a/test/etc/host-run-test-jar
+++ b/test/etc/host-run-test-jar
@@ -208,7 +208,16 @@
fi
cd $ANDROID_BUILD_TOP
-# If we are execing /bin/false we might not be on the same ISA as libsigchain.so
-# ld.so will helpfully warn us of this. Unfortunately this messes up our error
-# checking so we will just filter out the error with a grep.
-$mkdir_cmd && $prebuild_cmd && LD_PRELOAD=libsigchain.so $cmdline "$@" 2>&1 | grep -v -E "^ERROR: ld\.so: object '.+\.so' from LD_PRELOAD cannot be preloaded: ignored\.$"
+
+$mkdir_cmd || exit 1
+$prebuild_cmd || exit 2
+
+if [ "$GDB" = "y" ]; then
+ # When running under gdb, we cannot do piping and grepping...
+ LD_PRELOAD=libsigchain.so $cmdline "$@"
+else
+ # If we are execing /bin/false we might not be on the same ISA as libsigchain.so
+ # ld.so will helpfully warn us of this. Unfortunately this messes up our error
+ # checking so we will just filter out the error with a grep.
+ LD_PRELOAD=libsigchain.so $cmdline "$@" 2>&1 | grep -v -E "^ERROR: ld\.so: object '.+\.so' from LD_PRELOAD cannot be preloaded: ignored\.$"
+fi
\ No newline at end of file
diff --git a/tools/symbolize.sh b/tools/symbolize.sh
new file mode 100755
index 0000000..b66191f
--- /dev/null
+++ b/tools/symbolize.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# Symbolize oat files from the dalvik cache of a device.
+#
+# By default, pulls everything from the dalvik cache. A simple yes/no/quit prompt for each file can
+# be requested by giving "--interactive" as a parameter.
+
+INTERACTIVE="no"
+if [ "x$1" = "x--interactive" ] ; then
+ INTERACTIVE="yes"
+fi
+
+# Pull the file from the device and symbolize it.
+function one() {
+ echo $1 $2
+ if [ "x$INTERACTIVE" = "xyes" ] ; then
+ echo -n "What to do? [Y/n/q] "
+ read -e input
+ if [ "x$input" = "xn" ] ; then
+ return
+ fi
+ if [ "x$input" = "xq" ] ; then
+ exit 0
+ fi
+ fi
+ adb pull /data/dalvik-cache/$1/$2 /tmp || exit 1
+ mkdir -p $OUT/symbols/data/dalvik-cache/$1
+ oatdump --symbolize=/tmp/$2 --output=$OUT/symbols/data/dalvik-cache/$1/$2
+}
+
+# adb shell ls seems to output in DOS format (CRLF), which messes up scripting
+function adbls() {
+ adb shell ls $@ | sed 's/\r$//'
+}
+
+# Check for all ISA directories on device.
+function all() {
+ DIRS=$(adbls /data/dalvik-cache/)
+ for DIR in $DIRS ; do
+ case $DIR in
+ arm|arm64|mips|x86|x86_64)
+ FILES=$(adbls /data/dalvik-cache/$DIR/*.oat /data/dalvik-cache/$DIR/*.dex)
+ for FILE in $FILES ; do
+ # Cannot use basename as the file doesn't exist.
+ NAME=$(echo $FILE | sed -e 's/.*\///')
+ one $DIR $NAME
+ done
+ ;;
+ esac
+ done
+}
+
+all