diff options
Diffstat (limited to 'src/wasm')
-rw-r--r-- | src/wasm/wasm-debug.cpp | 135 |
1 files changed, 88 insertions, 47 deletions
diff --git a/src/wasm/wasm-debug.cpp b/src/wasm/wasm-debug.cpp index 4181b06f2..e90dc6d48 100644 --- a/src/wasm/wasm-debug.cpp +++ b/src/wasm/wasm-debug.cpp @@ -473,12 +473,18 @@ struct LocationUpdater { AddrExprMap oldExprAddrMap; FuncAddrMap oldFuncAddrMap; + // Map offsets of location list entries in the debug_loc section to the index + // of their compile unit. + std::unordered_map<BinaryLocation, size_t> locToUnitMap; + // Map start of line tables in the debug_line section to their new locations. - std::map<BinaryLocation, BinaryLocation> debugLineMap; + std::unordered_map<BinaryLocation, BinaryLocation> debugLineMap; + + typedef std::pair<BinaryLocation, BinaryLocation> OldToNew; - // Map offset of location list entries in the debug_loc section to the base - // address of the compilation units referencing them. - std::map<BinaryLocation, BinaryLocation> debugLocToUnitMap; + // Map of compile unit index => old and new base offsets (i.e., in the + // original binary and in the new one). + std::unordered_map<size_t, OldToNew> compileUnitBases; // TODO: for memory efficiency, we may want to do this in a streaming manner, // binary to binary, without YAML IR. @@ -618,8 +624,14 @@ struct LocationUpdater { return debugLineMap.at(old); } - BinaryLocation getLocationBaseAddress(BinaryLocation offset) const { - return debugLocToUnitMap.at(offset); + // Given an offset in .debug_loc, get the old and new compile unit bases. + OldToNew getCompileUnitBasesForLoc(size_t offset) const { + auto index = locToUnitMap.at(offset); + auto iter = compileUnitBases.find(index); + if (iter != compileUnitBases.end()) { + return iter->second; + } + return OldToNew{0, 0}; } }; @@ -756,7 +768,7 @@ static void updateDIE(const llvm::DWARFDebugInfoEntry& DIE, llvm::DWARFYAML::Entry& yamlEntry, const llvm::DWARFAbbreviationDeclaration* abbrevDecl, LocationUpdater& locationUpdater, - BinaryLocation compilationUnitBaseAddress) { + size_t compileUnitIndex) { auto tag = DIE.getTag(); // Pairs of low/high_pc require some special handling, as the high // may be an offset relative to the low. First, process everything but @@ -776,8 +788,13 @@ static void updateDIE(const llvm::DWARFDebugInfoEntry& DIE, tag == llvm::dwarf::DW_TAG_lexical_block || tag == llvm::dwarf::DW_TAG_label) { newValue = locationUpdater.getNewExprStart(oldValue); - } else if (tag == llvm::dwarf::DW_TAG_compile_unit || - tag == llvm::dwarf::DW_TAG_subprogram) { + } else if (tag == llvm::dwarf::DW_TAG_compile_unit) { + newValue = locationUpdater.getNewFuncStart(oldValue); + // Per the DWARF spec, "The base address of a compile unit is + // defined as the value of the DW_AT_low_pc attribute, if present." + locationUpdater.compileUnitBases[compileUnitIndex] = + LocationUpdater::OldToNew{oldValue, newValue}; + } else if (tag == llvm::dwarf::DW_TAG_subprogram) { newValue = locationUpdater.getNewFuncStart(oldValue); } else { Fatal() << "unknown tag with low_pc " @@ -793,8 +810,7 @@ static void updateDIE(const llvm::DWARFDebugInfoEntry& DIE, } else if (attr == llvm::dwarf::DW_AT_location && attrSpec.Form == llvm::dwarf::DW_FORM_sec_offset) { BinaryLocation locOffset = yamlValue.Value; - locationUpdater.debugLocToUnitMap[locOffset] = - compilationUnitBaseAddress; + locationUpdater.locToUnitMap[locOffset] = compileUnitIndex; } }); // Next, process the high_pcs. @@ -838,6 +854,7 @@ static void updateCompileUnits(const BinaryenDWARFInfo& info, LocationUpdater& locationUpdater) { // The context has the high-level information we need, and the YAML is where // we write changes. First, iterate over the compile units. + size_t compileUnitIndex = 0; iterContextAndYAML( info.context->compile_units(), yaml.CompileUnits, @@ -854,15 +871,11 @@ static void updateCompileUnits(const BinaryenDWARFInfo& info, auto abbrevDecl = DIE.getAbbreviationDeclarationPtr(); if (abbrevDecl) { // This is relevant; look for things to update. - BinaryLocation compilationUnitBaseAddress = - CU->getBaseAddress() ? CU->getBaseAddress()->Address : 0; - updateDIE(DIE, - yamlEntry, - abbrevDecl, - locationUpdater, - compilationUnitBaseAddress); + updateDIE( + DIE, yamlEntry, abbrevDecl, locationUpdater, compileUnitIndex); } }); + compileUnitIndex++; }); } @@ -907,59 +920,87 @@ static void updateRanges(llvm::DWARFYAML::Data& yaml, // would indicate an end or a base in .debug_loc). static const BinaryLocation IGNOREABLE_LOCATION = 1; +static bool isNewBaseLoc(const llvm::DWARFYAML::Loc& loc) { + return loc.Start == BinaryLocation(-1); +} + +static bool isEndMarkerLoc(const llvm::DWARFYAML::Loc& loc) { + return loc.Start == 0 && loc.End == 0; +} + // Update the .debug_loc section. static void updateLoc(llvm::DWARFYAML::Data& yaml, const LocationUpdater& locationUpdater) { // Similar to ranges, try to update the start and end. Note that here we // can't skip since the location description is a variable number of bytes, // so we mark no longer valid addresses as empty. - // Locations have an optional base. bool atStart = true; - BinaryLocation base = 0; - for (size_t i = 0; i < yaml.Locs.size(); i++) { - auto& loc = yaml.Locs[i]; + // We need to keep positions in the .debug_loc section identical to before + // (or else we'd need to update their positions too) and so we need to keep + // base entries around (a base entry is added to every entry after it in the + // list). However, we may change the base's value as after moving instructions + // around the old base may not be smaller than all the values relative to it. + BinaryLocation oldBase, newBase; + auto& locs = yaml.Locs; + for (size_t i = 0; i < locs.size(); i++) { + auto& loc = locs[i]; if (atStart) { - base = locationUpdater.getLocationBaseAddress(loc.CompileUnitOffset); + std::tie(oldBase, newBase) = + locationUpdater.getCompileUnitBasesForLoc(loc.CompileUnitOffset); atStart = false; } + // By default we copy values over, unless we modify them below. BinaryLocation newStart = loc.Start, newEnd = loc.End; - if (newStart == BinaryLocation(-1)) { + if (isNewBaseLoc(loc)) { // This is a new base. // Note that the base is not the address of an instruction, necessarily - // it's just a number (seems like it could always be an instruction, but // that's not what LLVM emits). - base = newEnd; - } else if (newStart == 0 && newEnd == 0) { - // This is an end marker, this list is done. - base = 0; + // We must look forward at everything relative to this base, so that we + // can emit a new proper base (as mentioned earlier, the original base may + // not be valid if instructions moved to a position before it - they must + // be positive offsets from it). + oldBase = newBase = newEnd; + BinaryLocation smallest = -1; + for (size_t j = i + 1; j < locs.size(); j++) { + auto& futureLoc = locs[j]; + if (isNewBaseLoc(futureLoc) || isEndMarkerLoc(futureLoc)) { + break; + } + auto updatedStart = + locationUpdater.getNewStart(futureLoc.Start + oldBase); + // If we found a valid mapping, this is a relevant value for us. If the + // optimizer removed it, it's a 0, and we can ignore it here - we will + // emit IGNOREABLE_LOCATION for it later anyhow. + if (updatedStart != 0) { + smallest = std::min(smallest, updatedStart); + } + } + // If we found no valid values that will be relativized here, just use 0 + // as the new (never-to-be-used) base, which is less confusing (otherwise + // the value looks like it means something). + if (smallest == BinaryLocation(-1)) { + smallest = 0; + } + newBase = newEnd = smallest; + } else if (isEndMarkerLoc(loc)) { + // This is an end marker, this list is done; reset the base. atStart = true; } else { // This is a normal entry, try to find what it should be updated to. First // de-relativize it to the base to get the absolute address, then look for // a new address for it. - newStart = locationUpdater.getNewStart(loc.Start + base); - newEnd = locationUpdater.getNewEnd(loc.End + base); + newStart = locationUpdater.getNewStart(loc.Start + oldBase); + newEnd = locationUpdater.getNewEnd(loc.End + oldBase); if (newStart == 0 || newEnd == 0) { // This part of the loc no longer has a mapping, so we must ignore it. newStart = newEnd = IGNOREABLE_LOCATION; } else { - // See if we can relativize it against the base. If things moved around - // so that the base is no longer before the start or the end, then we - // must skip this. That is, if we had something like - // - // [base] [start] [end] - // - // (where the X axis is the offset) and transforms led to - // - // [start] [base] [end] - // - // or such, then give up TODO: perhaps remap with a new base? - if (newStart >= base && newEnd >= base) { - newStart -= base; - newEnd -= base; - } else { - newStart = newEnd = IGNOREABLE_LOCATION; - } + // We picked a new base that ensures it is smaller than the values we + // will relativize to it. + assert(newStart >= newBase && newEnd >= newBase); + newStart -= newBase; + newEnd -= newBase; } // The loc start and end markers have been preserved. However, TODO // instructions in the middle may have moved around, making the loc no |