Please use GitHub pull requests for new patches. Avoid migrating existing patches. Phabricator shutdown timeline
Changeset View
Standalone View
lld/ELF/SymbolTable.cpp
Show First 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | |||||
// to the compiler at once, it can do whole-program optimization. | // to the compiler at once, it can do whole-program optimization. | ||||
template <class ELFT> void SymbolTable<ELFT>::addCombinedLTOObject() { | template <class ELFT> void SymbolTable<ELFT>::addCombinedLTOObject() { | ||||
if (BitcodeFiles.empty()) | if (BitcodeFiles.empty()) | ||||
return; | return; | ||||
// Compile bitcode files and replace bitcode symbols. | // Compile bitcode files and replace bitcode symbols. | ||||
LTO.reset(new BitcodeCompiler); | LTO.reset(new BitcodeCompiler); | ||||
for (BitcodeFile *F : BitcodeFiles) | for (BitcodeFile *F : BitcodeFiles) | ||||
LTO->add(*F); | LTO->add(*F); | ||||
pcc: If you move the list of renamed symbols to `Config` you can avoid this lambda. | |||||
Not Done ReplyInline ActionsOK, will do. dmikulin: OK, will do. | |||||
for (InputFile *File : LTO->compile()) { | for (InputFile *File : LTO->compile()) { | ||||
ObjectFile<ELFT> *Obj = cast<ObjectFile<ELFT>>(File); | ObjectFile<ELFT> *Obj = cast<ObjectFile<ELFT>>(File); | ||||
DenseSet<CachedHashStringRef> DummyGroups; | DenseSet<CachedHashStringRef> DummyGroups; | ||||
Obj->parse(DummyGroups); | Obj->parse(DummyGroups); | ||||
ObjectFiles.push_back(Obj); | ObjectFiles.push_back(Obj); | ||||
} | } | ||||
} | } | ||||
template <class ELFT> | template <class ELFT> | ||||
Convention says all Variable names should start with capital letter. Also, i is a little vague. davide: Convention says all Variable names should start with capital letter. Also, `i` is a little… | |||||
Not Done ReplyInline ActionsChanged it to RSI (Renamed Symbol Iterator) dmikulin: Changed it to RSI (Renamed Symbol Iterator) | |||||
DefinedRegular *SymbolTable<ELFT>::addAbsolute(StringRef Name, | DefinedRegular *SymbolTable<ELFT>::addAbsolute(StringRef Name, | ||||
uint8_t Visibility, | uint8_t Visibility, | ||||
Not Done ReplyInline ActionsDo you need an explicit iterator? Can't you use a range for ? davide: Do you need an explicit iterator? Can't you use a `range for` ? | |||||
uint8_t Binding) { | uint8_t Binding) { | ||||
Symbol *Sym = | Symbol *Sym = | ||||
addRegular(Name, Visibility, STT_NOTYPE, 0, 0, Binding, nullptr, nullptr); | addRegular(Name, Visibility, STT_NOTYPE, 0, 0, Binding, nullptr, nullptr); | ||||
return cast<DefinedRegular>(Sym->body()); | return cast<DefinedRegular>(Sym->body()); | ||||
} | } | ||||
// Add Name as an "ignored" symbol. An ignored symbol is a regular | // Add Name as an "ignored" symbol. An ignored symbol is a regular | ||||
// linker-synthesized defined symbol, but is only defined if needed. | // linker-synthesized defined symbol, but is only defined if needed. | ||||
template <class ELFT> | template <class ELFT> | ||||
DefinedRegular *SymbolTable<ELFT>::addIgnored(StringRef Name, | DefinedRegular *SymbolTable<ELFT>::addIgnored(StringRef Name, | ||||
uint8_t Visibility) { | uint8_t Visibility) { | ||||
SymbolBody *S = find(Name); | SymbolBody *S = find(Name); | ||||
if (!S || S->isInCurrentDSO()) | if (!S || S->isInCurrentDSO()) | ||||
return nullptr; | return nullptr; | ||||
return addAbsolute(Name, Visibility); | return addAbsolute(Name, Visibility); | ||||
} | } | ||||
// Set a flag for --trace-symbol so that we can print out a log message | // Set a flag for --trace-symbol so that we can print out a log message | ||||
// if a new symbol with the same name is inserted into the symbol table. | // if a new symbol with the same name is inserted into the symbol table. | ||||
template <class ELFT> void SymbolTable<ELFT>::trace(StringRef Name) { | template <class ELFT> void SymbolTable<ELFT>::trace(StringRef Name) { | ||||
Symtab.insert({CachedHashStringRef(Name), {-1, true}}); | Symtab.insert({CachedHashStringRef(Name), {-1, true}}); | ||||
} | } | ||||
// Get pre-LTO symbol binding for -wrap and -defsym symbols. | |||||
// If it's in the symbol table, read it from the symbol. | |||||
// Otherwise an undef for it will be created with STB_GLOBAL. | |||||
static uint8_t getSymbolBinding(SymbolBody *SB) { | |||||
pccUnsubmitted Is this function necessary? All callers pass a non-null SymbolBody, so they can just query the binding directly. pcc: Is this function necessary? All callers pass a non-null SymbolBody, so they can just query the… | |||||
uint8_t Binding = STB_GLOBAL; | |||||
if (SB) { | |||||
Not Done ReplyInline ActionsCan you explain why you need all three? davide: Can you explain why you need all three? | |||||
Not Done ReplyInline ActionsWe need all three if we want to match non-LTO behavior.
dmikulin: We need all three if we want to match non-LTO behavior.
* _wrap_bar is needed for the case… | |||||
Not Done ReplyInline ActionsCan you please add a test showing when 3) happens? davide: Can you please add a test showing when 3) happens? | |||||
Not Done ReplyInline Actions
dmikulin: 3) is also demonstrated by the test case in the PR. I'm working on regression tests for all… | |||||
Not Done ReplyInline Actions
This one only requires VisibleToRegularObj, doesn't it? pcc: > _wrap_bar is needed for the case where it is defined by the user but may be eliminated during… | |||||
Not Done ReplyInline ActionsLooks like it. Do we want to handle _wrap_* symbols specially and not set the weak binding for LTO? dmikulin: Looks like it. Do we want to handle _wrap_* symbols specially and not set the weak binding for… | |||||
Not Done ReplyInline ActionsUp to you. Note that you can make LTO keep a symbol by calling addUndefined with the symbol name, so it wouldn't be *that* special. pcc: Up to you. Note that you can make LTO keep a symbol by calling `addUndefined` with the symbol… | |||||
Symbol *S = SB->symbol(); | |||||
Binding = S->Binding; | |||||
} | |||||
return Binding; | |||||
Not Done ReplyInline ActionsWhat about the rename target? If I pass --defsym=foo=bar, the linker should not allow IPO past foo, right? pcc: What about the rename target? If I pass `--defsym=foo=bar`, the linker should not allow IPO… | |||||
Not Done ReplyInline ActionsRight. I'll add it to the list. dmikulin: Right. I'll add it to the list. | |||||
} | |||||
// Rename SYM as __wrap_SYM. The original symbol is preserved as __real_SYM. | // Rename SYM as __wrap_SYM. The original symbol is preserved as __real_SYM. | ||||
// Used to implement --wrap. | // Used to implement --wrap. | ||||
template <class ELFT> void SymbolTable<ELFT>::wrap(StringRef Name) { | template <class ELFT> void SymbolTable<ELFT>::addSymbolWrap(StringRef Name) { | ||||
SymbolBody *B = find(Name); | SymbolBody *SB = find(Name); | ||||
pccUnsubmitted I'd minimise the diff here and rename this variable back. pcc: I'd minimise the diff here and rename this variable back. | |||||
if (!B) | if (!SB) | ||||
return; | return; | ||||
Symbol *Sym = B->symbol(); | Symbol *Sym = SB->symbol(); | ||||
Symbol *Real = addUndefined(Saver.save("__real_" + Name)); | Symbol *Real = addUndefined(Saver.save("__real_" + Name)); | ||||
Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name)); | Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name)); | ||||
// Tell LTO not to eliminate this symbol | |||||
Not Done ReplyInline ActionsYou mean Alias here, right? pcc: You mean `Alias` here, right? | |||||
Not Done ReplyInline ActionsOf course. dmikulin: Of course. | |||||
Add a blank line before a comment. ruiu: Add a blank line before a comment. | |||||
Wrap->IsUsedInRegularObj = true; | |||||
You're setting IsUsedInRegularObj from what I can see, here, for all the symbols passed with --wrap. davide: You're setting `IsUsedInRegularObj` from what I can see, here, for all the symbols passed with… | |||||
// We rename symbols by replacing the old symbol's SymbolBody with the new | Config->RenamedSymbols[Real] = | ||||
// symbol's SymbolBody. This causes all SymbolBody pointers referring to the | RenamedSymbol{Sym, getSymbolBinding(Real->body())}; | ||||
// old symbol to instead refer to the new symbol. | Config->RenamedSymbols[Sym] = | ||||
pccUnsubmitted Move this comment to applySymbolRenames. pcc: Move this comment to applySymbolRenames. | |||||
memcpy(Real->Body.buffer, Sym->Body.buffer, sizeof(Sym->Body)); | RenamedSymbol{Wrap, getSymbolBinding(Sym->body())}; | ||||
memcpy(Sym->Body.buffer, Wrap->Body.buffer, sizeof(Wrap->Body)); | |||||
} | } | ||||
// Creates alias for symbol. Used to implement --defsym=ALIAS=SYM. | // Creates alias for symbol. Used to implement --defsym=ALIAS=SYM. | ||||
template <class ELFT> | template <class ELFT> void SymbolTable<ELFT>::addSymbolAlias(StringRef Alias, | ||||
Is this bit needed? I don't see a test for it. davide: Is this bit needed? I don't see a test for it. | |||||
Not Done ReplyInline ActionsIt was moved here from SymbolTable::alias, there is a test case for it already. dmikulin: It was moved here from SymbolTable::alias, there is a test case for it already. | |||||
void SymbolTable<ELFT>::alias(StringRef Alias, StringRef Name) { | StringRef Name) { | ||||
Not Done ReplyInline ActionsCan this ever be null? If so, you should check for it. davide: Can this ever be null? If so, you should check for it.
If not, you may as well inline this in… | |||||
Not Done ReplyInline ActionsIt can't be null, I just find it easier to read, that's all. dmikulin: It can't be null, I just find it easier to read, that's all. | |||||
SymbolBody *B = find(Name); | SymbolBody *SB = find(Name); | ||||
if (!B) { | if (!SB) { | ||||
error("-defsym: undefined symbol: " + Name); | error("-defsym: undefined symbol: " + Name); | ||||
return; | return; | ||||
} | } | ||||
Symbol *Sym = B->symbol(); | Symbol *Sym = SB->symbol(); | ||||
Symbol *AliasSym = addUndefined(Alias); | Symbol *AliasSym = addUndefined(Alias); | ||||
memcpy(AliasSym->Body.buffer, Sym->Body.buffer, sizeof(AliasSym->Body)); | // Tell LTO not to eliminate this symbol | ||||
Sym->IsUsedInRegularObj = true; | |||||
Same here. davide: Same here. | |||||
Config->RenamedSymbols[AliasSym] = | |||||
RenamedSymbol{Sym, getSymbolBinding(AliasSym->body())}; | |||||
} | |||||
// Apply symbol renames created by -wrap and -defsym | |||||
It is not obvious why you need this function from the source code. You need to add a comment. ruiu: It is not obvious why you need this function from the source code. You need to add a comment. | |||||
template <class ELFT> void SymbolTable<ELFT>::applySymbolRenames() { | |||||
What is RSI? I found just KV (short for key-value) makes sense in most cases. ruiu: What is RSI? I found just `KV` (short for key-value) makes sense in most cases. | |||||
Not Done ReplyInline ActionsRSI is supposed to be Renamed Symbol Iterator, but KV is ok with me too dmikulin: RSI is supposed to be Renamed Symbol Iterator, but KV is ok with me too | |||||
for (auto &RSI : Config->RenamedSymbols) { | |||||
Symbol *Sym = RSI.first; | |||||
Symbol *Rename = RSI.second.Target; | |||||
Sym->Binding = RSI.second.OrigBinding; | |||||
Ditto ruiu: Ditto | |||||
memcpy(Sym->Body.buffer, Rename->Body.buffer, sizeof(Sym->Body)); | |||||
} | |||||
Add a blank line before a comment. ruiu: Add a blank line before a comment. | |||||
} | } | ||||
Not Done ReplyInline ActionsCan we now replace these functions with something that applies the rename to each symbol in Config->RenamedSymbols? I think it should be possible with this change to how we declare RenamedSymbols. struct RenamedSymbol { uint8_t OrigBinding; Symbol *Target; }; MapVector<Symbol*, RenamedSymbol> RenamedSymbols; pcc: Can we now replace these functions with something that applies the rename to each symbol in… | |||||
Not Done ReplyInline ActionsBut __wrap_ symbols are different: they need to be on the list, but they are not being replaced. dmikulin: But __wrap_ symbols are different: they need to be on the list, but they are not being replaced. | |||||
Not Done ReplyInline ActionsThey only need to be marked as IsUsedInRegularObj. And that's taken care of for you by addUndefined. pcc: They only need to be marked as `IsUsedInRegularObj`. And that's taken care of for you by… | |||||
Not Done ReplyInline ActionsMap it to NULL? dmikulin: Map it to NULL? | |||||
Not Done ReplyInline ActionsI mean that you can just set IsUsedInRegularObj on the __wrap_ symbol before you assign it to Target. Then LTO will keep it alive. There's no need to add it (as a key) to RenamedSymbols then. pcc: I mean that you can just set `IsUsedInRegularObj` on the `__wrap_` symbol before you assign it… | |||||
Not Done ReplyInline ActionsThis works fine for -wrap, but not for -defsym. For -wrap, we want to keep the __real_ and the original symbols on the list, so the mappings are: For -defsym, we want to preserve the original symbol, so we would add Orig --> Alias mapping to communicate with LTO. But after we're done, we need to copy Orig --> Alias. I added a flag to the mapping structure to reverse the direction of the copy, so now in the -defsym test the call to bar3() gets inlined and the call to bar2() goes to its alias, bar3. This sounds reasonable to me, just want to make sure it's acceptable. dmikulin: This works fine for -wrap, but not for -defsym.
For -wrap, we want to keep the `__real_` and… | |||||
static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) { | static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) { | ||||
if (VA == STV_DEFAULT) | if (VA == STV_DEFAULT) | ||||
return VB; | return VB; | ||||
if (VB == STV_DEFAULT) | if (VB == STV_DEFAULT) | ||||
return VA; | return VA; | ||||
return std::min(VA, VB); | return std::min(VA, VB); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 126 Lines • ▼ Show 20 Lines | static int compareDefinedNonCommon(Symbol *S, bool WasInserted, uint8_t Binding, | ||||
bool IsAbsolute, typename ELFT::uint Value) { | bool IsAbsolute, typename ELFT::uint Value) { | ||||
if (int Cmp = compareDefined(S, WasInserted, Binding)) { | if (int Cmp = compareDefined(S, WasInserted, Binding)) { | ||||
if (Cmp > 0) | if (Cmp > 0) | ||||
S->Binding = Binding; | S->Binding = Binding; | ||||
return Cmp; | return Cmp; | ||||
} | } | ||||
SymbolBody *B = S->body(); | SymbolBody *B = S->body(); | ||||
if (isa<DefinedCommon>(B)) { | if (isa<DefinedCommon>(B)) { | ||||
// Non-common symbols take precedence over common symbols. | // Non-common symbols take precedence over common symbols. | ||||
if (Config->WarnCommon) | if (Config->WarnCommon) | ||||
warn("common " + S->body()->getName() + " is overridden"); | warn("common " + S->body()->getName() + " is overridden"); | ||||
Not Done ReplyInline Actionsempty ifs are not really common in llvm. davide: empty ifs are not really common in llvm. | |||||
Not Done ReplyInline ActionsI'll fix this. Negating the condition seamed counter-intuitive... dmikulin: I'll fix this. Negating the condition seamed counter-intuitive... | |||||
Not Done ReplyInline ActionsIs the purpose of this code just to restore the binding of the original symbol? I think it would be simpler to have a post-LTO step that manually restores the binding for each renamed symbol rather than dealing with it here. pcc: Is the purpose of this code just to restore the binding of the original symbol? I think it… | |||||
Not Done ReplyInline ActionsYes, it was to restore the binding. I changed it to have a separate pass over renamed symbols after LTO. dmikulin: Yes, it was to restore the binding. I changed it to have a separate pass over renamed symbols… | |||||
return 1; | return 1; | ||||
} else if (auto *R = dyn_cast<DefinedRegular>(B)) { | } else if (auto *R = dyn_cast<DefinedRegular>(B)) { | ||||
if (R->Section == nullptr && Binding == STB_GLOBAL && IsAbsolute && | if (R->Section == nullptr && Binding == STB_GLOBAL && IsAbsolute && | ||||
R->Value == Value) | R->Value == Value) | ||||
return -1; | return -1; | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 398 Lines • Show Last 20 Lines |
If you move the list of renamed symbols to Config you can avoid this lambda.