Expand Up
@@ -172,7 +172,9 @@ class Writer {
std::map<std::pair<StringRef, uint32_t >, std::vector<Chunk *>> &Map);
void createExportTable ();
void mergeSections ();
void readRelocTargets ();
void assignAddresses ();
void finalizeAddresses ();
void removeEmptySections ();
void createSymbolAndStringTable ();
void openFile (StringRef OutputPath);
Expand Down
Expand Up
@@ -299,6 +301,193 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) {
} // namespace coff
} // namespace lld
// Check whether the target address S is in range from a relocation
// of type RelType at address P.
static bool isInRange (uint16_t RelType, uint64_t S, uint64_t P, int Margin) {
assert (Config->Machine == ARMNT);
int64_t Diff = AbsoluteDifference (S, P + 4 ) + Margin;
switch (RelType) {
case IMAGE_REL_ARM_BRANCH20T:
return isInt<21 >(Diff);
case IMAGE_REL_ARM_BRANCH24T:
case IMAGE_REL_ARM_BLX23T:
return isInt<25 >(Diff);
default :
return true ;
}
}
// Return the last thunk for the given target if it is in range,
// or create a new one.
static std::pair<Defined *, bool >
getThunk (DenseMap<uint64_t , Defined *> &LastThunks, Defined *Target, uint64_t P,
uint16_t Type, int Margin) {
Defined *&LastThunk = LastThunks[Target->getRVA ()];
if (LastThunk && isInRange (Type, LastThunk->getRVA (), P, Margin))
return {LastThunk, false };
RangeExtensionThunk *C = make<RangeExtensionThunk>(Target);
Defined *D = make<DefinedSynthetic>(" " , C);
LastThunk = D;
return {D, true };
}
// This checks all relocations, and for any relocation which isn't in range
// it adds a thunk after the section chunk that contains the relocation.
// If the latest thunk for the specific target is in range, that is used
// instead of creating a new thunk. All range checks are done with the
// specified margin, to make sure that relocations that originally are in
// range, but only barely, also get thunks - in case other added thunks makes
// the target go out of range.
//
// After adding thunks, we verify that all relocations are in range (with
// no extra margin requirements). If this failed, we restart (throwing away
// the previously created thunks) and retry with a wider margin.
static bool createThunks (std::vector<Chunk *> &Chunks, int Margin) {
bool AddressesChanged = false ;
DenseMap<uint64_t , Defined *> LastThunks;
size_t ThunksSize = 0 ;
// Recheck Chunks.size() each iteration, since we can insert more
// elements into it.
for (size_t I = 0 ; I != Chunks.size (); ++I) {
SectionChunk *SC = dyn_cast_or_null<SectionChunk>(Chunks[I]);
if (!SC)
continue ;
size_t ThunkInsertionSpot = I + 1 ;
// Try to get a good enough estimate of where new thunks will be placed.
// Offset this by the size of the new thunks added so far, to make the
// estimate slightly better.
size_t ThunkInsertionRVA = SC->getRVA () + SC->getSize () + ThunksSize;
for (size_t J = 0 , E = SC->Relocs .size (); J < E; ++J) {
const coff_relocation &Rel = SC->Relocs [J];
Symbol *&RelocTarget = SC->RelocTargets [J];
// The estimate of the source address P should be pretty accurate,
// but we don't know whether the target Symbol address should be
// offset by ThunkSize or not (or by some of ThunksSize but not all of
// it), giving us some uncertainty once we have added one thunk.
uint64_t P = SC->getRVA () + Rel.VirtualAddress + ThunksSize;
Defined *Sym = dyn_cast_or_null<Defined>(RelocTarget);
if (!Sym)
continue ;
uint64_t S = Sym->getRVA ();
if (isInRange (Rel.Type , S, P, Margin))
continue ;
// If the target isn't in range, hook it up to an existing or new
// thunk.
Defined *Thunk;
bool WasNew;
std::tie (Thunk, WasNew) = getThunk (LastThunks, Sym, P, Rel.Type , Margin);
if (WasNew) {
Chunk *ThunkChunk = Thunk->getChunk ();
ThunkChunk->setRVA (
ThunkInsertionRVA); // Estimate of where it will be located.
Chunks.insert (Chunks.begin () + ThunkInsertionSpot, ThunkChunk);
ThunkInsertionSpot++;
ThunksSize += ThunkChunk->getSize ();
ThunkInsertionRVA += ThunkChunk->getSize ();
AddressesChanged = true ;
}
RelocTarget = Thunk;
}
}
return AddressesChanged;
}
// Verify that all relocations are in range, with no extra margin requirements.
static bool verifyRanges (const std::vector<Chunk *> Chunks) {
for (Chunk *C : Chunks) {
SectionChunk *SC = dyn_cast_or_null<SectionChunk>(C);
if (!SC)
continue ;
for (size_t J = 0 , E = SC->Relocs .size (); J < E; ++J) {
const coff_relocation &Rel = SC->Relocs [J];
Symbol *RelocTarget = SC->RelocTargets [J];
Defined *Sym = dyn_cast_or_null<Defined>(RelocTarget);
if (!Sym)
continue ;
uint64_t P = SC->getRVA () + Rel.VirtualAddress ;
uint64_t S = Sym->getRVA ();
if (!isInRange (Rel.Type , S, P, 0 ))
return false ;
}
}
return true ;
}
// Assign addresses and add thunks if necessary.
void Writer::finalizeAddresses () {
assignAddresses ();
if (Config->Machine != ARMNT)
return ;
size_t OrigNumChunks = 0 ;
for (OutputSection *Sec : OutputSections) {
Sec->OrigChunks = Sec->Chunks ;
OrigNumChunks += Sec->Chunks .size ();
}
int Pass = 0 ;
int Margin = 1024 * 100 ;
while (true ) {
// First check whether we need thunks at all, or if the previous pass of
// adding them turned out ok.
bool RangesOk = true ;
size_t NumChunks = 0 ;
for (OutputSection *Sec : OutputSections) {
if (!verifyRanges (Sec->Chunks )) {
RangesOk = false ;
break ;
}
NumChunks += Sec->Chunks .size ();
}
if (RangesOk) {
if (Pass > 0 )
log (" Added " + Twine (NumChunks - OrigNumChunks) + " thunks with " +
" margin " + Twine (Margin) + " in " + Twine (Pass) + " passes" );
return ;
}
if (Pass >= 10 )
fatal (" adding thunks hasn't converged after " + Twine (Pass) + " passes" );
if (Pass > 0 ) {
// If the previous pass didn't work out, reset everything back to the
// original conditions before retrying with a wider margin. This should
// ideally never happen under real circumstances.
for (OutputSection *Sec : OutputSections) {
Sec->Chunks = Sec->OrigChunks ;
for (Chunk *C : Sec->Chunks )
C->resetRelocTargets ();
}
Margin *= 2 ;
}
// Try adding thunks everywhere where it is needed, with a margin
// to avoid things going out of range due to the added thunks.
bool AddressesChanged = false ;
for (OutputSection *Sec : OutputSections)
AddressesChanged |= createThunks (Sec->Chunks , Margin);
// If the verification above thought we needed thunks, we should have
// added some.
assert (AddressesChanged);
// Recalculate the layout for the whole image (and verify the ranges at
// the start of the next round).
assignAddresses ();
Pass++;
}
}
// The main function of the writer.
void Writer::run () {
ScopedTimer T1 (CodeLayoutTimer);
Expand All
@@ -309,7 +498,8 @@ void Writer::run() {
appendImportThunks ();
createExportTable ();
mergeSections ();
assignAddresses ();
readRelocTargets ();
finalizeAddresses ();
removeEmptySections ();
setSectionPermissions ();
createSymbolAndStringTable ();
Expand Down
Expand Up
@@ -796,9 +986,9 @@ void Writer::createSymbolAndStringTable() {
}
void Writer::mergeSections () {
if (!PdataSec->getChunks () .empty ()) {
FirstPdata = PdataSec->getChunks () .front ();
LastPdata = PdataSec->getChunks () .back ();
if (!PdataSec->Chunks .empty ()) {
FirstPdata = PdataSec->Chunks .front ();
LastPdata = PdataSec->Chunks .back ();
}
for (auto &P : Config->Merge ) {
Expand Down
Expand Up
@@ -826,6 +1016,13 @@ void Writer::mergeSections() {
}
}
// Visits all sections to initialize their relocation targets.
void Writer::readRelocTargets () {
for (OutputSection *Sec : OutputSections)
for_each (parallel::par, Sec->Chunks .begin (), Sec->Chunks .end (),
[&](Chunk *C) { C->readRelocTargets (); });
}
// Visits all sections to assign incremental, non-overlapping RVAs and
// file offsets.
void Writer::assignAddresses () {
Expand All
@@ -843,7 +1040,7 @@ void Writer::assignAddresses() {
addBaserels ();
uint64_t RawSize = 0 , VirtualSize = 0 ;
Sec->Header .VirtualAddress = RVA;
for (Chunk *C : Sec->getChunks () ) {
for (Chunk *C : Sec->Chunks ) {
VirtualSize = alignTo (VirtualSize, C->Alignment );
C->setRVA (RVA + VirtualSize);
C->OutputSectionOff = VirtualSize;
Expand Down
Expand Up
@@ -1315,7 +1512,7 @@ void Writer::writeSections() {
// ADD instructions).
if (Sec->Header .Characteristics & IMAGE_SCN_CNT_CODE)
memset (SecBuf, 0xCC , Sec->getRawSize ());
for_each (parallel::par, Sec->getChunks () .begin (), Sec->getChunks () .end (),
for_each (parallel::par, Sec->Chunks .begin (), Sec->Chunks .end (),
[&](Chunk *C) { C->writeTo (SecBuf); });
}
}
Expand Down
Expand Up
@@ -1399,12 +1596,13 @@ uint32_t Writer::getSizeOfInitializedData() {
void Writer::addBaserels () {
if (!Config->Relocatable )
return ;
RelocSec->Chunks .clear ();
std::vector<Baserel> V;
for (OutputSection *Sec : OutputSections) {
if (Sec->Header .Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
continue ;
// Collect all locations for base relocations.
for (Chunk *C : Sec->getChunks () )
for (Chunk *C : Sec->Chunks )
C->getBaserels (&V);
// Add the addresses to .reloc section.
if (!V.empty ())
Expand Down