This is an archive of the discontinued LLVM Phabricator instance.

Implement --cref.
ClosedPublic

Authored by ruiu on Mar 9 2018, 4:26 PM.

Details

Summary

This is an option to print out a table of symbols and filenames.
The output format of this option is the same as GNU, so that it can be
processed by the same scripts as before after migrating from GNU to lld.

This option is mildly useful; we can live without it. But it is pretty
convenient sometimes, and it can be implemented in 50 lines of code, so
I think lld should support this option.

Diff Detail

Repository
rL LLVM

Event Timeline

ruiu created this revision.Mar 9 2018, 4:26 PM
echristo accepted this revision.Mar 9 2018, 4:37 PM

Awesome.

-eric

This revision is now accepted and ready to land.Mar 9 2018, 4:37 PM
ruiu updated this revision to Diff 137873.Mar 9 2018, 4:49 PM
  • do not crash when printing out an absolute symbol.

GNU spec says "If a linker map file is being generated, the cross reference table is printed to the map file.", I checked that bfd do that.
Should we do that too?

lld/ELF/MapFile.cpp
174 ↗(On Diff #137873)

Your test pass with this line removed.

190 ↗(On Diff #137873)

I would move Files below Print.

193 ↗(On Diff #137873)

Does it make sense to sort Files by name? Can be convenient probably. (BFD does not do that FWIW).

Rui Ueyama via Phabricator via llvm-commits
<llvm-commits@lists.llvm.org> writes:

+defm cref: B<"cref",
+ "Output cross reference table",
+ "Do not output cross reference table">;
+

Why B? Is there a --no-cref?

+ Output a cross reference table to stdout. This is for --cref.
+

+ For each global symbol, we print out a file that defines the symbol
+
followed by files that uses that symbol. Here is an example.
+
+
strlen /lib/x86_64-linux-gnu/libc.so.6
+ tools/lld/tools/lld/CMakeFiles/lld.dir/lld.cpp.o
+
lib/libLLVMSupport.a(PrettyStackTrace.cpp.o)
+
+
In this case, strlen is defined by libc.so.6 and used by other two
+ files.
+void elf::writeCrossReferenceTable() {
+ if (!Config->Cref)
+ return;
+
+
Collect symbols and files.
+ MapVector<Symbol *, SetVector<InputFile *>> Map;
+ for (InputFile *File : ObjectFiles) {
+ for (Symbol *B : File->getSymbols()) {

I would probably avoid B since it is a strange choice now that
SymbolBody is gone.

+ if (auto *Sym = dyn_cast<SharedSymbol>(B))

This can be an isa<>, no?

+ Map[Sym].insert(File);
+ if (auto *Sym = dyn_cast<Defined>(B))
+ if (!Sym->isLocal() || (!Sym->Section || Sym->Section->Live))
+ Map[Sym].insert(File);
+ }
+ }
+
+ auto Print = [](StringRef A, StringRef B) {
+ outs() << left_justify(A, 49) << " " << B << "\n";
+ };

This doesn't need to be a lambda.

Cheers,
Rafael

ruiu added a comment.Mar 14 2018, 12:48 PM

Why B? Is there a --no-cref?

Yes, gold has that.

GNU spec says "If a linker map file is being generated, the cross reference table is printed to the map file.", I checked that bfd do that. Should we do that too?

I think we shouldn't. Our map file format is different from GNU, but this -cref format is compatible, and mixing the two produces an output that can't be parsed by existing tools.

lld/ELF/MapFile.cpp
174 ↗(On Diff #137873)

Fixed.

190 ↗(On Diff #137873)

I wouldn't because I want to make the type explicit as the beginning of this loop.

193 ↗(On Diff #137873)

I was thinking about that, but I decided to not. I can convinced the other way, but I feel like sorting files alphabetically doesn't make much sense in cref file.

ruiu updated this revision to Diff 138440.Mar 14 2018, 12:48 PM
  • address review comments.

LGTM

Rui Ueyama via Phabricator <reviews@reviews.llvm.org> writes:

ruiu updated this revision to Diff 138440.
ruiu added a comment.

  • address review comments.

https://reviews.llvm.org/D44336

Files:

lld/ELF/Config.h
lld/ELF/Driver.cpp
lld/ELF/MapFile.cpp
lld/ELF/MapFile.h
lld/ELF/Options.td
lld/ELF/Writer.cpp
lld/test/ELF/cref.s
lld/test/ELF/silent-ignore.test

Index: lld/test/ELF/silent-ignore.test

  • lld/test/ELF/silent-ignore.test

+++ lld/test/ELF/silent-ignore.test
@@ -1,6 +1,5 @@
RUN: ld.lld --version \
RUN: -allow-shlib-undefined \
-RUN: -cref \
RUN: -g \
RUN: -no-add-needed \
RUN: -no-allow-shlib-undefined \

Index: lld/test/ELF/cref.s

  • /dev/null

+++ lld/test/ELF/cref.s
@@ -0,0 +1,26 @@
+ REQUIRES: x86
+
+
RUN: echo '.global foo; foo:' | llvm-mc -filetype=obj -triple=x86_64-pc-linux - -o %t1.o
+ RUN: echo '.global foo, bar; bar:' | llvm-mc -filetype=obj -triple=x86_64-pc-linux - -o %t2.o
+
RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t3.o
+ RUN: ld.lld -shared -o %t1.so %t1.o -gc-sections
+
RUN: ld.lld -o /dev/null %t1.so %t2.o %t3.o -cref | FileCheck -strict-whitespace %s
+
+ CHECK: Symbol File
+
CHECK-NEXT: bar {{.*}}2.o
+ CHECK-NEXT: {{.*}}3.o
+
CHECK-NEXT: foo {{.*}}1.so
+ CHECK-NEXT: {{.*}}2.o
+
CHECK-NEXT: {{.*}}3.o
+ CHECK-NEXT: _start {{.*}}3.o
+
CHECK-NEXT: baz {{.*}}3.o
+
+.global _start, foo, bar, baz
+_start:
+ call foo
+ call bar
+localsym:
+baz:
+
+.section .text.a,"ax",@progbits
+discarded:

Index: lld/ELF/Writer.cpp

  • lld/ELF/Writer.cpp

+++ lld/ELF/Writer.cpp
@@ -475,8 +475,9 @@

if (errorCount())
  return;
  • // Handle -Map option.

+ // Handle -Map and -cref options.

writeMapFile();

+ writeCrossReferenceTable();

if (errorCount())
  return;

Index: lld/ELF/Options.td

  • lld/ELF/Options.td

+++ lld/ELF/Options.td
@@ -75,6 +75,10 @@
def color_diagnostics_eq: J<"color-diagnostics=">,

HelpText<"Use colors in diagnostics">;

+defm cref: B<"cref",
+ "Output cross reference table",
+ "Do not output cross reference table">;
+
defm define_common: B<"define-common",

"Assign space to common symbols",
"Do not assign space to common symbols">;

@@ -420,7 +424,6 @@

// Options listed below are silently ignored for now for compatibility.
def allow_shlib_undefined: F<"allow-shlib-undefined">;
-def cref: F<"cref">;
def detect_odr_violations: F<"detect-odr-violations">;
def g: Flag<["-"], "g">;
def long_plt: F<"long-plt">;

Index: lld/ELF/MapFile.h

  • lld/ELF/MapFile.h

+++ lld/ELF/MapFile.h
@@ -13,6 +13,7 @@
namespace lld {
namespace elf {
void writeMapFile();
+void writeCrossReferenceTable();
} namespace elf
}
namespace lld

Index: lld/ELF/MapFile.cpp

  • lld/ELF/MapFile.cpp

+++ lld/ELF/MapFile.cpp
@@ -28,6 +28,8 @@
#include "SyntheticSections.h"
#include "lld/Common/Strings.h"
#include "lld/Common/Threads.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SetVector.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;
@@ -146,3 +148,50 @@

  }
}

}
+
+static void print(StringRef A, StringRef B) {
+ outs() << left_justify(A, 49) << " " << B << "\n";
+}
+
+ Output a cross reference table to stdout. This is for --cref.
+

+ For each global symbol, we print out a file that defines the symbol
+
followed by files that uses that symbol. Here is an example.
+
+
strlen /lib/x86_64-linux-gnu/libc.so.6
+ tools/lld/tools/lld/CMakeFiles/lld.dir/lld.cpp.o
+
lib/libLLVMSupport.a(PrettyStackTrace.cpp.o)
+
+
In this case, strlen is defined by libc.so.6 and used by other two
+ files.
+void elf::writeCrossReferenceTable() {
+ if (!Config->Cref)
+ return;
+
+
Collect symbols and files.
+ MapVector<Symbol *, SetVector<InputFile *>> Map;
+ for (InputFile *File : ObjectFiles) {
+ for (Symbol *Sym : File->getSymbols()) {
+ if (isa<SharedSymbol>(Sym))
+ Map[Sym].insert(File);
+ if (auto *D = dyn_cast<Defined>(Sym))
+ if (!D->isLocal() && (!D->Section || D->Section->Live))
+ Map[D].insert(File);
+ }
+ }
+
+ Print out a header.
+ outs() << "Cross Reference Table\n\n";
+ print("Symbol", "File");
+
+
Print out a table.
+ for (auto KV : Map) {
+ Symbol *Sym = KV.first;
+ SetVector<InputFile *> &Files = KV.second;
+
+ print(toString(*Sym), toString(Sym->File));
+ for (InputFile *File : Files)
+ if (File != Sym->File)
+ print("", toString(File));
+ }
+}

Index: lld/ELF/Driver.cpp

  • lld/ELF/Driver.cpp

+++ lld/ELF/Driver.cpp
@@ -618,6 +618,7 @@

    Args.hasFlag(OPT_check_sections, OPT_no_check_sections, true);
Config->Chroot = Args.getLastArgValue(OPT_chroot);
Config->CompressDebugSections = getCompressDebugSections(Args);

+ Config->Cref = Args.hasFlag(OPT_cref, OPT_no_cref, false);

Config->DefineCommon = Args.hasFlag(OPT_define_common, OPT_no_define_common,
                                    !Args.hasArg(OPT_relocatable));
Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);

Index: lld/ELF/Config.h

  • lld/ELF/Config.h

+++ lld/ELF/Config.h
@@ -113,6 +113,7 @@

bool BsymbolicFunctions;
bool CheckSections;
bool CompressDebugSections;

+ bool Cref;

bool DefineCommon;
bool Demangle = true;
bool DisableVerify;
This revision was automatically updated to reflect the committed changes.