1 //=== DIEAttributeCloner.cpp ----------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "DIEAttributeCloner.h"
10 #include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h"
13 namespace dwarflinker_parallel
{
15 void DIEAttributeCloner::clone() {
16 DWARFUnit
&U
= CU
.getOrigUnit();
18 // Extract and clone every attribute.
19 DWARFDataExtractor Data
= U
.getDebugInfoExtractor();
21 uint64_t Offset
= InputDieEntry
->getOffset();
22 // Point to the next DIE (generally there is always at least a NULL
23 // entry after the current one). If this is a lone
24 // DW_TAG_compile_unit without any children, point to the next unit.
25 uint64_t NextOffset
= (InputDIEIdx
+ 1 < U
.getNumDIEs())
26 ? U
.getDIEAtIndex(InputDIEIdx
+ 1).getOffset()
27 : U
.getNextUnitOffset();
29 // We could copy the data only if we need to apply a relocation to it. After
30 // testing, it seems there is no performance downside to doing the copy
31 // unconditionally, and it makes the code simpler.
32 SmallString
<40> DIECopy(Data
.getData().substr(Offset
, NextOffset
- Offset
));
34 DWARFDataExtractor(DIECopy
, Data
.isLittleEndian(), Data
.getAddressSize());
36 // Modify the copy with relocated addresses.
37 CU
.getContaingFile().Addresses
->applyValidRelocs(DIECopy
, Offset
,
38 Data
.isLittleEndian());
40 // Reset the Offset to 0 as we will be working on the local copy of
44 const auto *Abbrev
= InputDieEntry
->getAbbreviationDeclarationPtr();
45 Offset
+= getULEB128Size(Abbrev
->getCode());
47 // Set current output offset.
48 AttrOutOffset
= OutDIE
->getOffset();
49 for (const auto &AttrSpec
: Abbrev
->attributes()) {
50 // Check whether current attribute should be skipped.
51 if (shouldSkipAttribute(AttrSpec
)) {
52 DWARFFormValue::skipValue(AttrSpec
.Form
, Data
, &Offset
,
57 DWARFFormValue Val
= AttrSpec
.getFormValue();
58 Val
.extractValue(Data
, &Offset
, U
.getFormParams(), &U
);
60 // Clone current attribute.
61 switch (AttrSpec
.Form
) {
62 case dwarf::DW_FORM_strp
:
63 case dwarf::DW_FORM_line_strp
:
64 case dwarf::DW_FORM_string
:
65 case dwarf::DW_FORM_strx
:
66 case dwarf::DW_FORM_strx1
:
67 case dwarf::DW_FORM_strx2
:
68 case dwarf::DW_FORM_strx3
:
69 case dwarf::DW_FORM_strx4
:
70 AttrOutOffset
+= cloneStringAttr(Val
, AttrSpec
);
72 case dwarf::DW_FORM_ref_addr
:
73 case dwarf::DW_FORM_ref1
:
74 case dwarf::DW_FORM_ref2
:
75 case dwarf::DW_FORM_ref4
:
76 case dwarf::DW_FORM_ref8
:
77 case dwarf::DW_FORM_ref_udata
:
78 AttrOutOffset
+= cloneDieRefAttr(Val
, AttrSpec
);
80 case dwarf::DW_FORM_data1
:
81 case dwarf::DW_FORM_data2
:
82 case dwarf::DW_FORM_data4
:
83 case dwarf::DW_FORM_data8
:
84 case dwarf::DW_FORM_udata
:
85 case dwarf::DW_FORM_sdata
:
86 case dwarf::DW_FORM_sec_offset
:
87 case dwarf::DW_FORM_flag
:
88 case dwarf::DW_FORM_flag_present
:
89 case dwarf::DW_FORM_rnglistx
:
90 case dwarf::DW_FORM_loclistx
:
91 case dwarf::DW_FORM_implicit_const
:
92 AttrOutOffset
+= cloneScalarAttr(Val
, AttrSpec
);
94 case dwarf::DW_FORM_block
:
95 case dwarf::DW_FORM_block1
:
96 case dwarf::DW_FORM_block2
:
97 case dwarf::DW_FORM_block4
:
98 case dwarf::DW_FORM_exprloc
:
99 AttrOutOffset
+= cloneBlockAttr(Val
, AttrSpec
);
101 case dwarf::DW_FORM_addr
:
102 case dwarf::DW_FORM_addrx
:
103 case dwarf::DW_FORM_addrx1
:
104 case dwarf::DW_FORM_addrx2
:
105 case dwarf::DW_FORM_addrx3
:
106 case dwarf::DW_FORM_addrx4
:
107 AttrOutOffset
+= cloneAddressAttr(Val
, AttrSpec
);
110 CU
.warn("unsupported attribute form " +
111 dwarf::FormEncodingString(AttrSpec
.Form
) +
112 " in DieAttributeCloner::clone(). Dropping.",
117 // We convert source strings into the indexed form for DWARFv5.
118 // Check if original compile unit already has DW_AT_str_offsets_base
120 if (InputDieEntry
->getTag() == dwarf::DW_TAG_compile_unit
&&
121 CU
.getVersion() >= 5 && !AttrInfo
.HasStringOffsetBaseAttr
) {
122 DebugInfoOutputSection
.notePatchWithOffsetUpdate(
125 &CU
.getOrCreateSectionDescriptor(DebugSectionKind::DebugStrOffsets
),
129 AttrOutOffset
+= Generator
130 .addScalarAttribute(dwarf::DW_AT_str_offsets_base
,
131 dwarf::DW_FORM_sec_offset
,
132 CU
.getDebugStrOffsetsHeaderSize())
137 bool DIEAttributeCloner::shouldSkipAttribute(
138 DWARFAbbreviationDeclaration::AttributeSpec AttrSpec
) {
139 switch (AttrSpec
.Attr
) {
142 case dwarf::DW_AT_low_pc
:
143 case dwarf::DW_AT_high_pc
:
144 case dwarf::DW_AT_ranges
:
145 if (CU
.getGlobalData().getOptions().UpdateIndexTablesOnly
)
148 // Skip address attribute if we are in function scope and function does not
149 // reference live address.
150 return CU
.getDIEInfo(InputDIEIdx
).getIsInFunctionScope() &&
151 !FuncAddressAdjustment
.has_value();
152 case dwarf::DW_AT_rnglists_base
:
153 // In case !Update the .debug_addr table is not generated/preserved.
154 // Thus instead of DW_FORM_rnglistx the DW_FORM_sec_offset is used.
155 // Since DW_AT_rnglists_base is used for only DW_FORM_rnglistx the
156 // DW_AT_rnglists_base is removed.
157 return !CU
.getGlobalData().getOptions().UpdateIndexTablesOnly
;
158 case dwarf::DW_AT_loclists_base
:
159 // In case !Update the .debug_addr table is not generated/preserved.
160 // Thus instead of DW_FORM_loclistx the DW_FORM_sec_offset is used.
161 // Since DW_AT_loclists_base is used for only DW_FORM_loclistx the
162 // DW_AT_loclists_base is removed.
163 return !CU
.getGlobalData().getOptions().UpdateIndexTablesOnly
;
164 case dwarf::DW_AT_location
:
165 case dwarf::DW_AT_frame_base
:
166 if (CU
.getGlobalData().getOptions().UpdateIndexTablesOnly
)
169 // When location expression contains an address: skip this attribute
170 // if it does not reference live address.
171 if (HasLocationExpressionAddress
)
172 return !VarAddressAdjustment
.has_value();
174 // Skip location attribute if we are in function scope and function does not
175 // reference live address.
176 return CU
.getDIEInfo(InputDIEIdx
).getIsInFunctionScope() &&
177 !FuncAddressAdjustment
.has_value();
181 size_t DIEAttributeCloner::cloneStringAttr(
182 const DWARFFormValue
&Val
,
183 const DWARFAbbreviationDeclaration::AttributeSpec
&AttrSpec
) {
184 std::optional
<const char *> String
= dwarf::toString(Val
);
186 CU
.warn("cann't read string attribute.");
190 StringEntry
*StringInPool
=
191 CU
.getGlobalData().getStringPool().insert(*String
).first
;
192 if (AttrSpec
.Form
== dwarf::DW_FORM_line_strp
) {
193 DebugInfoOutputSection
.notePatchWithOffsetUpdate(
194 DebugLineStrPatch
{{AttrOutOffset
}, StringInPool
}, PatchesOffsets
);
196 .addStringPlaceholderAttribute(AttrSpec
.Attr
, dwarf::DW_FORM_line_strp
)
200 // Update attributes info.
201 if (AttrSpec
.Attr
== dwarf::DW_AT_name
)
202 AttrInfo
.Name
= StringInPool
;
203 else if (AttrSpec
.Attr
== dwarf::DW_AT_MIPS_linkage_name
||
204 AttrSpec
.Attr
== dwarf::DW_AT_linkage_name
)
205 AttrInfo
.MangledName
= StringInPool
;
207 if (CU
.getVersion() < 5) {
208 DebugInfoOutputSection
.notePatchWithOffsetUpdate(
209 DebugStrPatch
{{AttrOutOffset
}, StringInPool
}, PatchesOffsets
);
212 .addStringPlaceholderAttribute(AttrSpec
.Attr
, dwarf::DW_FORM_strp
)
217 .addIndexedStringAttribute(AttrSpec
.Attr
, dwarf::DW_FORM_strx
,
218 CU
.getDebugStrIndex(StringInPool
))
222 size_t DIEAttributeCloner::cloneDieRefAttr(
223 const DWARFFormValue
&Val
,
224 const DWARFAbbreviationDeclaration::AttributeSpec
&AttrSpec
) {
225 if (AttrSpec
.Attr
== dwarf::DW_AT_sibling
)
228 std::optional
<std::pair
<CompileUnit
*, uint32_t>> RefDiePair
=
229 CU
.resolveDIEReference(Val
);
231 // If the referenced DIE is not found, drop the attribute.
232 CU
.warn("cann't find referenced DIE.", InputDieEntry
);
235 assert(RefDiePair
->first
->getStage() >= CompileUnit::Stage::Loaded
);
236 assert(RefDiePair
->second
!= 0);
238 // Get output offset for referenced DIE.
239 uint64_t OutDieOffset
=
240 RefDiePair
->first
->getDieOutOffset(RefDiePair
->second
);
242 // Examine whether referenced DIE is in current compile unit.
243 bool IsLocal
= CU
.getUniqueID() == RefDiePair
->first
->getUniqueID();
245 // Set attribute form basing on the kind of referenced DIE(local or not?).
246 dwarf::Form NewForm
= IsLocal
? dwarf::DW_FORM_ref4
: dwarf::DW_FORM_ref_addr
;
248 // Check whether current attribute references already cloned DIE inside
249 // the same compilation unit. If true - write the already known offset value.
250 if (IsLocal
&& (OutDieOffset
!= 0))
251 return Generator
.addScalarAttribute(AttrSpec
.Attr
, NewForm
, OutDieOffset
)
254 // If offset value is not known at this point then create patch for the
255 // reference value and write dummy value into the attribute.
256 DebugInfoOutputSection
.notePatchWithOffsetUpdate(
257 DebugDieRefPatch
{AttrOutOffset
, &CU
, RefDiePair
->first
,
260 return Generator
.addScalarAttribute(AttrSpec
.Attr
, NewForm
, 0xBADDEF).second
;
263 size_t DIEAttributeCloner::cloneScalarAttr(
264 const DWARFFormValue
&Val
,
265 const DWARFAbbreviationDeclaration::AttributeSpec
&AttrSpec
) {
267 // Create patches for attribute referencing other non invariant section.
268 // Invariant section could not be updated here as this section and
269 // reference to it do not change value in case --update.
270 if (AttrSpec
.Attr
== dwarf::DW_AT_macro_info
) {
271 if (std::optional
<uint64_t> Offset
= Val
.getAsSectionOffset()) {
272 const DWARFDebugMacro
*Macro
=
273 CU
.getContaingFile().Dwarf
->getDebugMacinfo();
274 if (Macro
== nullptr || !Macro
->hasEntryForOffset(*Offset
))
277 DebugInfoOutputSection
.notePatchWithOffsetUpdate(
278 DebugOffsetPatch
{AttrOutOffset
, &CU
.getOrCreateSectionDescriptor(
279 DebugSectionKind::DebugMacinfo
)},
282 } else if (AttrSpec
.Attr
== dwarf::DW_AT_macros
) {
283 if (std::optional
<uint64_t> Offset
= Val
.getAsSectionOffset()) {
284 const DWARFDebugMacro
*Macro
=
285 CU
.getContaingFile().Dwarf
->getDebugMacro();
286 if (Macro
== nullptr || !Macro
->hasEntryForOffset(*Offset
))
289 DebugInfoOutputSection
.notePatchWithOffsetUpdate(
290 DebugOffsetPatch
{AttrOutOffset
, &CU
.getOrCreateSectionDescriptor(
291 DebugSectionKind::DebugMacro
)},
294 } else if (AttrSpec
.Attr
== dwarf::DW_AT_stmt_list
) {
295 DebugInfoOutputSection
.notePatchWithOffsetUpdate(
296 DebugOffsetPatch
{AttrOutOffset
, &CU
.getOrCreateSectionDescriptor(
297 DebugSectionKind::DebugLine
)},
299 } else if (AttrSpec
.Attr
== dwarf::DW_AT_str_offsets_base
) {
300 DebugInfoOutputSection
.notePatchWithOffsetUpdate(
303 &CU
.getOrCreateSectionDescriptor(DebugSectionKind::DebugStrOffsets
),
307 // Use size of .debug_str_offsets header as attribute value. The offset
308 // to .debug_str_offsets would be added later while patching.
309 AttrInfo
.HasStringOffsetBaseAttr
= true;
311 .addScalarAttribute(AttrSpec
.Attr
, AttrSpec
.Form
,
312 CU
.getDebugStrOffsetsHeaderSize())
317 if (CU
.getGlobalData().getOptions().UpdateIndexTablesOnly
) {
318 if (auto OptionalValue
= Val
.getAsUnsignedConstant())
319 Value
= *OptionalValue
;
320 else if (auto OptionalValue
= Val
.getAsSignedConstant())
321 Value
= *OptionalValue
;
322 else if (auto OptionalValue
= Val
.getAsSectionOffset())
323 Value
= *OptionalValue
;
325 CU
.warn("unsupported scalar attribute form. Dropping attribute.",
330 if (AttrSpec
.Attr
== dwarf::DW_AT_declaration
&& Value
)
331 AttrInfo
.IsDeclaration
= true;
333 if (AttrSpec
.Form
== dwarf::DW_FORM_loclistx
)
334 return Generator
.addLocListAttribute(AttrSpec
.Attr
, AttrSpec
.Form
, Value
)
337 return Generator
.addScalarAttribute(AttrSpec
.Attr
, AttrSpec
.Form
, Value
)
341 dwarf::Form ResultingForm
= AttrSpec
.Form
;
342 if (AttrSpec
.Form
== dwarf::DW_FORM_rnglistx
) {
343 // DWARFLinker does not generate .debug_addr table. Thus we need to change
344 // all "addrx" related forms to "addr" version. Change DW_FORM_rnglistx
345 // to DW_FORM_sec_offset here.
346 std::optional
<uint64_t> Index
= Val
.getAsSectionOffset();
348 CU
.warn("cann't read the attribute. Dropping.", InputDieEntry
);
351 std::optional
<uint64_t> Offset
= CU
.getOrigUnit().getRnglistOffset(*Index
);
353 CU
.warn("cann't read the attribute. Dropping.", InputDieEntry
);
358 ResultingForm
= dwarf::DW_FORM_sec_offset
;
359 } else if (AttrSpec
.Form
== dwarf::DW_FORM_loclistx
) {
360 // DWARFLinker does not generate .debug_addr table. Thus we need to change
361 // all "addrx" related forms to "addr" version. Change DW_FORM_loclistx
362 // to DW_FORM_sec_offset here.
363 std::optional
<uint64_t> Index
= Val
.getAsSectionOffset();
365 CU
.warn("cann't read the attribute. Dropping.", InputDieEntry
);
368 std::optional
<uint64_t> Offset
= CU
.getOrigUnit().getLoclistOffset(*Index
);
370 CU
.warn("cann't read the attribute. Dropping.", InputDieEntry
);
375 ResultingForm
= dwarf::DW_FORM_sec_offset
;
376 } else if (AttrSpec
.Attr
== dwarf::DW_AT_high_pc
&&
377 InputDieEntry
->getTag() == dwarf::DW_TAG_compile_unit
) {
378 std::optional
<uint64_t> LowPC
= CU
.getLowPc();
381 // Dwarf >= 4 high_pc is an size, not an address.
382 Value
= CU
.getHighPc() - *LowPC
;
383 } else if (AttrSpec
.Form
== dwarf::DW_FORM_sec_offset
)
384 Value
= *Val
.getAsSectionOffset();
385 else if (AttrSpec
.Form
== dwarf::DW_FORM_sdata
)
386 Value
= *Val
.getAsSignedConstant();
387 else if (auto OptionalValue
= Val
.getAsUnsignedConstant())
388 Value
= *OptionalValue
;
390 CU
.warn("unsupported scalar attribute form. Dropping attribute.",
395 if (AttrSpec
.Attr
== dwarf::DW_AT_ranges
||
396 AttrSpec
.Attr
== dwarf::DW_AT_start_scope
) {
397 // Create patch for the range offset value.
398 DebugInfoOutputSection
.notePatchWithOffsetUpdate(
399 DebugRangePatch
{{AttrOutOffset
},
400 InputDieEntry
->getTag() == dwarf::DW_TAG_compile_unit
},
402 AttrInfo
.HasRanges
= true;
403 } else if (DWARFAttribute::mayHaveLocationList(AttrSpec
.Attr
) &&
404 dwarf::doesFormBelongToClass(AttrSpec
.Form
,
405 DWARFFormValue::FC_SectionOffset
,
406 CU
.getOrigUnit().getVersion())) {
407 int64_t AddrAdjustmentValue
= 0;
408 if (VarAddressAdjustment
)
409 AddrAdjustmentValue
= *VarAddressAdjustment
;
410 else if (FuncAddressAdjustment
)
411 AddrAdjustmentValue
= *FuncAddressAdjustment
;
413 // Create patch for the location offset value.
414 DebugInfoOutputSection
.notePatchWithOffsetUpdate(
415 DebugLocPatch
{{AttrOutOffset
}, AddrAdjustmentValue
}, PatchesOffsets
);
416 } else if (AttrSpec
.Attr
== dwarf::DW_AT_addr_base
) {
417 DebugInfoOutputSection
.notePatchWithOffsetUpdate(
420 &CU
.getOrCreateSectionDescriptor(DebugSectionKind::DebugAddr
),
424 // Use size of .debug_addr header as attribute value. The offset to
425 // .debug_addr would be added later while patching.
427 .addScalarAttribute(AttrSpec
.Attr
, AttrSpec
.Form
,
428 CU
.getDebugAddrHeaderSize())
430 } else if (AttrSpec
.Attr
== dwarf::DW_AT_declaration
&& Value
)
431 AttrInfo
.IsDeclaration
= true;
433 return Generator
.addScalarAttribute(AttrSpec
.Attr
, ResultingForm
, Value
)
437 size_t DIEAttributeCloner::cloneBlockAttr(
438 const DWARFFormValue
&Val
,
439 const DWARFAbbreviationDeclaration::AttributeSpec
&AttrSpec
) {
441 size_t NumberOfPatchesAtStart
= PatchesOffsets
.size();
443 // If the block is a DWARF Expression, clone it into the temporary
444 // buffer using cloneExpression(), otherwise copy the data directly.
445 SmallVector
<uint8_t, 32> Buffer
;
446 ArrayRef
<uint8_t> Bytes
= *Val
.getAsBlock();
447 if (DWARFAttribute::mayHaveLocationExpr(AttrSpec
.Attr
) &&
448 (Val
.isFormClass(DWARFFormValue::FC_Block
) ||
449 Val
.isFormClass(DWARFFormValue::FC_Exprloc
))) {
450 DWARFUnit
&OrigUnit
= CU
.getOrigUnit();
451 DataExtractor
Data(StringRef((const char *)Bytes
.data(), Bytes
.size()),
452 OrigUnit
.isLittleEndian(),
453 OrigUnit
.getAddressByteSize());
454 DWARFExpression
Expr(Data
, OrigUnit
.getAddressByteSize(),
455 OrigUnit
.getFormParams().Format
);
457 CU
.cloneDieAttrExpression(Expr
, Buffer
, DebugInfoOutputSection
,
458 VarAddressAdjustment
, PatchesOffsets
);
462 // The expression location data might be updated and exceed the original size.
463 // Check whether the new data fits into the original form.
464 dwarf::Form ResultForm
= AttrSpec
.Form
;
465 if ((ResultForm
== dwarf::DW_FORM_block1
&& Bytes
.size() > UINT8_MAX
) ||
466 (ResultForm
== dwarf::DW_FORM_block2
&& Bytes
.size() > UINT16_MAX
) ||
467 (ResultForm
== dwarf::DW_FORM_block4
&& Bytes
.size() > UINT32_MAX
))
468 ResultForm
= dwarf::DW_FORM_block
;
470 size_t FinalAttributeSize
;
471 if (AttrSpec
.Form
== dwarf::DW_FORM_exprloc
)
473 Generator
.addLocationAttribute(AttrSpec
.Attr
, ResultForm
, Bytes
).second
;
476 Generator
.addBlockAttribute(AttrSpec
.Attr
, ResultForm
, Bytes
).second
;
478 // Update patches offsets with the size of length field for Bytes.
479 for (size_t Idx
= NumberOfPatchesAtStart
; Idx
< PatchesOffsets
.size();
481 assert(FinalAttributeSize
> Bytes
.size());
482 *PatchesOffsets
[Idx
] +=
483 (AttrOutOffset
+ (FinalAttributeSize
- Bytes
.size()));
486 return FinalAttributeSize
;
489 size_t DIEAttributeCloner::cloneAddressAttr(
490 const DWARFFormValue
&Val
,
491 const DWARFAbbreviationDeclaration::AttributeSpec
&AttrSpec
) {
492 if (AttrSpec
.Attr
== dwarf::DW_AT_low_pc
)
493 AttrInfo
.HasLowPc
= true;
495 if (CU
.getGlobalData().getOptions().UpdateIndexTablesOnly
)
497 .addScalarAttribute(AttrSpec
.Attr
, AttrSpec
.Form
, Val
.getRawUValue())
500 // Cloned Die may have address attributes relocated to a
501 // totally unrelated value. This can happen:
502 // - If high_pc is an address (Dwarf version == 2), then it might have been
503 // relocated to a totally unrelated value (because the end address in the
504 // object file might be start address of another function which got moved
505 // independently by the linker).
506 // - If address relocated in an inline_subprogram that happens at the
507 // beginning of its inlining function.
508 // To avoid above cases and to not apply relocation twice (in
509 // applyValidRelocs and here), read address attribute from InputDIE and apply
510 // Info.PCOffset here.
512 std::optional
<DWARFFormValue
> AddrAttribute
=
513 CU
.find(InputDieEntry
, AttrSpec
.Attr
);
515 llvm_unreachable("Cann't find attribute");
517 std::optional
<uint64_t> Addr
= AddrAttribute
->getAsAddress();
519 CU
.warn("cann't read address attribute value.");
523 if (InputDieEntry
->getTag() == dwarf::DW_TAG_compile_unit
&&
524 AttrSpec
.Attr
== dwarf::DW_AT_low_pc
) {
525 if (std::optional
<uint64_t> LowPC
= CU
.getLowPc())
529 } else if (InputDieEntry
->getTag() == dwarf::DW_TAG_compile_unit
&&
530 AttrSpec
.Attr
== dwarf::DW_AT_high_pc
) {
531 if (uint64_t HighPc
= CU
.getHighPc())
536 if (VarAddressAdjustment
)
537 *Addr
+= *VarAddressAdjustment
;
538 else if (FuncAddressAdjustment
)
539 *Addr
+= *FuncAddressAdjustment
;
542 if (AttrSpec
.Form
== dwarf::DW_FORM_addr
) {
543 return Generator
.addScalarAttribute(AttrSpec
.Attr
, AttrSpec
.Form
, *Addr
)
548 .addScalarAttribute(AttrSpec
.Attr
, dwarf::Form::DW_FORM_addrx
,
549 CU
.getDebugAddrIndex(*Addr
))
553 unsigned DIEAttributeCloner::finalizeAbbreviations(bool HasChildrenToClone
) {
554 size_t SizeOfAbbreviationNumber
=
555 Generator
.finalizeAbbreviations(HasChildrenToClone
);
557 // We need to update patches offsets after we know the size of the
558 // abbreviation number.
559 updatePatchesWithSizeOfAbbreviationNumber(SizeOfAbbreviationNumber
);
561 // Add the size of the abbreviation number to the output offset.
562 AttrOutOffset
+= SizeOfAbbreviationNumber
;
564 return AttrOutOffset
;
567 } // end of namespace dwarflinker_parallel