From 2f96d8255dedc1999dfacbb5515596d107e5b790 Mon Sep 17 00:00:00 2001 From: Parth Arora Date: Tue, 26 May 2026 07:55:21 -0700 Subject: [PATCH] Add --emit-load-info-json feature --- include/eld/Config/GeneralOptions.h | 10 + include/eld/Diagnostics/DiagCommonKinds.inc | 2 +- include/eld/Driver/GnuLinkerOptions.td | 6 + include/eld/Input/Input.h | 13 + include/eld/Object/LoadInfoReport.h | 53 ++++ lib/Core/Module.cpp | 3 + lib/Input/Input.cpp | 2 + lib/LinkerWrapper/GnuLdDriver.cpp | 28 +- lib/Object/CMakeLists.txt | 1 + lib/Object/LoadInfoReport.cpp | 249 ++++++++++++++++++ .../EmitLoadInfoJson/EmitLoadInfoJson.test | 43 +++ .../standalone/EmitLoadInfoJson/Inputs/bar.c | 1 + .../standalone/EmitLoadInfoJson/Inputs/foo.c | 3 + .../standalone/EmitLoadInfoJson/Inputs/lib.c | 1 + .../standalone/EmitLoadInfoJson/Inputs/main.c | 3 + 15 files changed, 410 insertions(+), 8 deletions(-) create mode 100644 include/eld/Object/LoadInfoReport.h create mode 100644 lib/Object/LoadInfoReport.cpp create mode 100644 test/Common/standalone/EmitLoadInfoJson/EmitLoadInfoJson.test create mode 100644 test/Common/standalone/EmitLoadInfoJson/Inputs/bar.c create mode 100644 test/Common/standalone/EmitLoadInfoJson/Inputs/foo.c create mode 100644 test/Common/standalone/EmitLoadInfoJson/Inputs/lib.c create mode 100644 test/Common/standalone/EmitLoadInfoJson/Inputs/main.c diff --git a/include/eld/Config/GeneralOptions.h b/include/eld/Config/GeneralOptions.h index 612cac70f..d0efb89d2 100644 --- a/include/eld/Config/GeneralOptions.h +++ b/include/eld/Config/GeneralOptions.h @@ -529,6 +529,15 @@ class GeneralOptions { return ArchiveMemberReportFile; } + // --emit-load-info-json + void setEmitLoadInfoJsonFile(llvm::StringRef File) { + EmitLoadInfoJsonFile = File.str(); + } + + const std::optional &getEmitLoadInfoJsonFile() const { + return EmitLoadInfoJsonFile; + } + // --ld-generated-unwind-info void setGenUnwindInfo(bool PEnable = true) { BGenUnwindInfo = PEnable; } @@ -1426,6 +1435,7 @@ class GeneralOptions { std::string LinkLaunchDirectory; bool ShowRMSectNameInDiag = false; bool UseDefaultPlugins = true; + std::optional EmitLoadInfoJsonFile; }; } // namespace eld diff --git a/include/eld/Diagnostics/DiagCommonKinds.inc b/include/eld/Diagnostics/DiagCommonKinds.inc index e6d6b3d92..9e86dd00e 100644 --- a/include/eld/Diagnostics/DiagCommonKinds.inc +++ b/include/eld/Diagnostics/DiagCommonKinds.inc @@ -121,7 +121,7 @@ DIAG(plugin_crash, DiagnosticEngine::Fatal, DIAG(unable_to_create_temporary_file, DiagnosticEngine::Fatal, "Unable to create temporary file %0") DIAG(unable_to_write_json_file, DiagnosticEngine::Error, - "Unable to write JSON file %0 due to error %1") + "Unable to write file %0 due to %1") DIAG(unsupported_linker_flavor, DiagnosticEngine::Error, "Unsupported linker flavor %0") DIAG(unable_to_compress, DiagnosticEngine::Fatal, "Unable to compress %0") diff --git a/include/eld/Driver/GnuLinkerOptions.td b/include/eld/Driver/GnuLinkerOptions.td index 637c181c5..028aa054e 100644 --- a/include/eld/Driver/GnuLinkerOptions.td +++ b/include/eld/Driver/GnuLinkerOptions.td @@ -1374,6 +1374,12 @@ defm ArchiveMemberReportFile MetaVarName<"">, Group; +defm EmitLoadInfoJson + : smDashWithOpt<"emit-load-info-json", "EmitLoadInfoJson", + "Emit JSON report containing information about all input files">, + MetaVarName<"">, + Group; + //===----------------------------------------------------------------------===// /// Help! //===----------------------------------------------------------------------===// diff --git a/include/eld/Input/Input.h b/include/eld/Input/Input.h index 37f811209..875546016 100644 --- a/include/eld/Input/Input.h +++ b/include/eld/Input/Input.h @@ -181,6 +181,16 @@ class Input { static void cacheMemoryAreaForPath(const std::string &Filepath, MemoryArea *Area); + /// Set the ordinal value that marks the end of internal inputs. + /// This should be called after all internal inputs are created. + static void setInternalInputOrderEnd(uint32_t OrderEnd) { + InternalInputOrderEnd = OrderEnd; + } + + /// Get the ordinal value that marks the end of internal inputs. + /// User input ordinals start from this value. + static uint32_t getInternalInputOrderEnd() { return InternalInputOrderEnd; } + private: // Check if a path is valid and emit any errors bool isPathValid(const std::string &Path) const; @@ -215,6 +225,9 @@ class Input { /// the link command-line. static std::unordered_map ResolvedPathToMemoryAreaMap; + + /// The ordinal value marking the end of internal inputs. + static uint32_t InternalInputOrderEnd; }; } // namespace eld diff --git a/include/eld/Object/LoadInfoReport.h b/include/eld/Object/LoadInfoReport.h new file mode 100644 index 000000000..59fefe316 --- /dev/null +++ b/include/eld/Object/LoadInfoReport.h @@ -0,0 +1,53 @@ +//===- LoadInfoReport.h---------------------------------------------------===// +// Part of the eld Project, under the BSD License +// See https://github.com/qualcomm/eld/LICENSE.txt for license information. +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#ifndef ELD_OBJECT_LOADINFOREPORT_H +#define ELD_OBJECT_LOADINFOREPORT_H + +#include "eld/Object/ObjectLinker.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/JSON.h" +#include +#include + +namespace eld { + +class ArchiveFile; +class DiagnosticEngine; +class Input; +class InputAction; +class LinkerScriptFile; + +class LoadInfoReport { +public: + LoadInfoReport(const ObjectLinker &ObjLinker, + const std::vector &Actions, + DiagnosticEngine &DiagEngine); + + bool emitLoadInfoJson(llvm::StringRef Filename); + +private: + using ArchiveRecordsMapType = + std::unordered_map>; + + ArchiveRecordsMapType buildArchiveRecordsMap(); + llvm::StringRef getTypeName(InputFile::InputFileKind Kind); + llvm::StringRef getBehaviorOptionName(InputAction::InputActionKind Kind); + bool isBehaviorOption(InputAction::InputActionKind Kind); + llvm::json::Object emitInputEntry(Input *I, bool IncludeIndex); + void emitArchiveMembers(llvm::json::Object &Entry, ArchiveFile *AF); + void emitNestedInputs(llvm::json::Object &Entry, LinkerScriptFile *LSF); + + const ObjectLinker &ObjLinker; + const std::vector &Actions; + DiagnosticEngine &DiagEngine; + ArchiveRecordsMapType ArchiveRecordsMap; +}; + +} // namespace eld + +#endif // ELD_OBJECT_LOADINFOREPORT_H diff --git a/lib/Core/Module.cpp b/lib/Core/Module.cpp index aa98a21d4..ee4097493 100644 --- a/lib/Core/Module.cpp +++ b/lib/Core/Module.cpp @@ -266,6 +266,9 @@ bool Module::createInternalInputs() { ResolvedResult.Info->setOutSymbol(DotSym); setDotSymbol(DotSym); + Input::setInternalInputOrderEnd( + static_cast(Module::InternalInputType::MAX)); + return true; } diff --git a/lib/Input/Input.cpp b/lib/Input/Input.cpp index 98d0714a7..54a255e56 100644 --- a/lib/Input/Input.cpp +++ b/lib/Input/Input.cpp @@ -246,6 +246,8 @@ llvm::hash_code Input::computeFilePathHash(llvm::StringRef FilePath) { std::unordered_map Input::ResolvedPathToMemoryAreaMap; +uint32_t Input::InternalInputOrderEnd = 0; + MemoryArea *Input::getMemoryAreaForPath(const std::string &Filepath, DiagnosticEngine *DiagEngine) { auto Iter = ResolvedPathToMemoryAreaMap.find(Filepath); diff --git a/lib/LinkerWrapper/GnuLdDriver.cpp b/lib/LinkerWrapper/GnuLdDriver.cpp index 5027aec9b..46fad1150 100644 --- a/lib/LinkerWrapper/GnuLdDriver.cpp +++ b/lib/LinkerWrapper/GnuLdDriver.cpp @@ -26,6 +26,7 @@ #include "eld/LayoutMap/TextLayoutPrinter.h" #include "eld/LayoutMap/YamlLayoutPrinter.h" #include "eld/Object/ArchiveMemberReport.h" +#include "eld/Object/LoadInfoReport.h" #include "eld/Support/FileSystem.h" #include "eld/Support/MappingFileReader.h" #include "eld/Support/MsgHandling.h" @@ -62,11 +63,12 @@ using namespace eld; static constexpr llvm::opt::OptTable::Info infoTable[] = { #define OPTION(PREFIXES_OFFSET, PREFIXED_NAME_OFFSET, ID, KIND, GROUP, ALIAS, \ ALIASARGS, FLAGS, VISIBILITY, PARAM, HELPTEXT, \ - HELPTEXTSFORVARIANTS, METAVAR, VALUES, SUBCOMMANDIDS_OFFSET) \ - LLVM_CONSTRUCT_OPT_INFO( \ - PREFIXES_OFFSET, PREFIXED_NAME_OFFSET, GnuLdOptTable::ID, KIND, \ - GnuLdOptTable::GROUP, GnuLdOptTable::ALIAS, ALIASARGS, FLAGS, \ - VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES, SUBCOMMANDIDS_OFFSET), + HELPTEXTSFORVARIANTS, METAVAR, VALUES, SUBCOMMANDIDS_OFFSET) \ + LLVM_CONSTRUCT_OPT_INFO(PREFIXES_OFFSET, PREFIXED_NAME_OFFSET, \ + GnuLdOptTable::ID, KIND, GnuLdOptTable::GROUP, \ + GnuLdOptTable::ALIAS, ALIASARGS, FLAGS, VISIBILITY, \ + PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \ + VALUES, SUBCOMMANDIDS_OFFSET), #include "eld/Driver/GnuLinkerOptions.inc" #undef OPTION }; @@ -601,7 +603,8 @@ bool GnuLdDriver::processOptions(llvm::opt::InputArgList &Args) { // --enable-linker-version / --disable-linker-version if (Args.hasArg(T::enable_linker_version) && Args.hasArg(T::disable_linker_version)) { - errs() << "Cannot specify enable and disable LINKER_VERSION at same time!\n"; + errs() + << "Cannot specify enable and disable LINKER_VERSION at same time!\n"; return false; } @@ -612,7 +615,8 @@ bool GnuLdDriver::processOptions(llvm::opt::InputArgList &Args) { if (Args.hasArg(T::disable_linker_version)) { Config.options().setLinkerVersionDirectiveEnabled(false); - Config.addCommandLine(Table->getOptionName(T::disable_linker_version), true); + Config.addCommandLine(Table->getOptionName(T::disable_linker_version), + true); } bool recordCommandLine = Args.hasFlag( @@ -1302,6 +1306,11 @@ bool GnuLdDriver::processOptions(llvm::opt::InputArgList &Args) { Config.options().setArchiveMemberReportFile(A->getValue()); } + // --emit-load-info-json= + if (llvm::opt::Arg *A = Args.getLastArg(T::EmitLoadInfoJson)) { + Config.options().setEmitLoadInfoJsonFile(A->getValue()); + } + Config.options().setUnknownOptions(Args.getAllArgValues(T::UNKNOWN)); return true; } @@ -1991,6 +2000,11 @@ bool GnuLdDriver::doLink(llvm::opt::InputArgList &Args, } if (!linkStatus || Config.options().getRecordInputFiles()) handleReproduce(Args, actions, true); + if (auto &LoadInfoFile = Config.options().getEmitLoadInfoJsonFile()) { + LoadInfoReport Report(*linker.getObjectLinker(), actions, + *Config.getDiagEngine()); + Report.emitLoadInfoJson(LoadInfoFile.value()); + } if (auto &PluginActLog = ThisModule->getPluginActivityLog()) { PluginActLog->print(Config.options().getPluginActivityLogFile().value()); } diff --git a/lib/Object/CMakeLists.txt b/lib/Object/CMakeLists.txt index 18274d595..52dbf3d26 100644 --- a/lib/Object/CMakeLists.txt +++ b/lib/Object/CMakeLists.txt @@ -10,6 +10,7 @@ llvm_add_library( RuleContainer.cpp SectionMap.cpp ArchiveMemberReport.cpp + LoadInfoReport.cpp DEPENDS intrinsics_gen) diff --git a/lib/Object/LoadInfoReport.cpp b/lib/Object/LoadInfoReport.cpp new file mode 100644 index 000000000..305136a3f --- /dev/null +++ b/lib/Object/LoadInfoReport.cpp @@ -0,0 +1,249 @@ +//===- LoadInfoReport.cpp-------------------------------------------------===// +// Part of the eld Project, under the BSD License +// See https://github.com/qualcomm/eld/LICENSE.txt for license information. +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include "eld/Object/LoadInfoReport.h" +#include "eld/Config/Version.h" +#include "eld/Diagnostics/DiagnosticEngine.h" +#include "eld/Input/ArchiveFile.h" +#include "eld/Input/ArchiveMemberInput.h" +#include "eld/Input/ELFDynObjectFile.h" +#include "eld/Input/InputAction.h" +#include "eld/Input/InputFile.h" +#include "eld/Input/InputTree.h" +#include "eld/Input/LinkerScriptFile.h" +#include "eld/SymbolResolver/LDSymbol.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + +using namespace eld; + +LoadInfoReport::LoadInfoReport(const ObjectLinker &ObjLinker, + const std::vector &Actions, + DiagnosticEngine &DiagEngine) + : ObjLinker(ObjLinker), Actions(Actions), DiagEngine(DiagEngine) { + ArchiveRecordsMap = buildArchiveRecordsMap(); +} + +LoadInfoReport::ArchiveRecordsMapType LoadInfoReport::buildArchiveRecordsMap() { + ArchiveRecordsMapType Result; + for (const auto &Record : ObjLinker.getArchiveRecordsForReport()) { + if (!Record.Origin) + continue; + if (auto *AMI = llvm::dyn_cast(Record.Origin)) { + ArchiveFile *AF = AMI->getArchiveFile(); + if (AF) + Result[AF].push_back(Record); + } + } + return Result; +} + +llvm::StringRef LoadInfoReport::getTypeName(InputFile::InputFileKind Kind) { + switch (Kind) { + case InputFile::ELFObjFileKind: + return "Object"; + case InputFile::ELFDynObjFileKind: + return "SharedLibrary"; + case InputFile::GNUArchiveFileKind: + return "Archive"; + case InputFile::GNULinkerScriptKind: + return "LinkerScript"; + case InputFile::BitcodeFileKind: + return "BitcodeFile"; + case InputFile::BinaryFileKind: + return "BinaryFile"; + case InputFile::ELFExecutableFileKind: + return "Executable"; + default: + return "Unknown"; + } +} + +llvm::StringRef +LoadInfoReport::getBehaviorOptionName(InputAction::InputActionKind Kind) { + switch (Kind) { + case InputAction::AsNeeded: + return "--as-needed"; + case InputAction::NoAsNeeded: + return "--no-as-needed"; + case InputAction::WholeArchive: + return "--whole-archive"; + case InputAction::NoWholeArchive: + return "--no-whole-archive"; + case InputAction::StartGroup: + return "--start-group"; + case InputAction::EndGroup: + return "--end-group"; + case InputAction::StartLib: + return "--start-lib"; + case InputAction::EndLib: + return "--end-lib"; + case InputAction::PushState: + return "--push-state"; + case InputAction::PopState: + return "--pop-state"; + case InputAction::BStatic: + return "-Bstatic"; + case InputAction::BDynamic: + return "-Bdynamic"; + case InputAction::AddNeeded: + return "--add-needed"; + case InputAction::NoAddNeeded: + return "--no-add-needed"; + case InputAction::InputFormat: + return "--format"; + default: + return ""; + } +} + +bool LoadInfoReport::isBehaviorOption(InputAction::InputActionKind Kind) { + switch (Kind) { + case InputAction::AsNeeded: + case InputAction::NoAsNeeded: + case InputAction::WholeArchive: + case InputAction::NoWholeArchive: + case InputAction::StartGroup: + case InputAction::EndGroup: + case InputAction::StartLib: + case InputAction::EndLib: + case InputAction::PushState: + case InputAction::PopState: + case InputAction::BStatic: + case InputAction::BDynamic: + case InputAction::AddNeeded: + case InputAction::NoAddNeeded: + case InputAction::InputFormat: + return true; + default: + return false; + } +} + +void LoadInfoReport::emitArchiveMembers(llvm::json::Object &Entry, + ArchiveFile *AF) { + Entry["IncludedCount"] = static_cast(AF->getLoadedMemberCount()); + Entry["TotalCount"] = static_cast(AF->getTotalMemberCount()); + + auto It = ArchiveRecordsMap.find(AF); + if (It == ArchiveRecordsMap.end()) + return; + + llvm::json::Array Members; + for (const auto &Record : It->second) { + llvm::json::Object Member; + if (auto *AMI = llvm::dyn_cast(Record.Origin)) + Member["Name"] = AMI->getMemberName().str(); + else + Member["Name"] = Record.Origin->getFileName(); + + if (Record.Referrer == nullptr && Record.Symbol == nullptr) { + Member["Reason"] = "WholeArchive"; + } else if (Record.Symbol && Record.Referrer) { + std::string Reason = + llvm::formatv("Referred by {0}({1})", + Record.Referrer->getInput()->decoratedPath(false), + Record.Symbol->name()) + .str(); + Member["Reason"] = Reason; + } else if (Record.Symbol) { + Member["Reason"] = Record.Symbol->name(); + } + Members.push_back(std::move(Member)); + } + if (!Members.empty()) + Entry["Members"] = std::move(Members); +} + +void LoadInfoReport::emitNestedInputs(llvm::json::Object &Entry, + LinkerScriptFile *LSF) { + llvm::json::Array NestedInputs; + for (Node *N : LSF->getNodes()) { + if (auto *FN = llvm::dyn_cast(N)) { + Input *NestedI = FN->getInput(); + if (!NestedI) + continue; + NestedInputs.push_back(emitInputEntry(NestedI, /*IncludeIndex=*/false)); + } + } + Entry["NestedInputs"] = std::move(NestedInputs); +} + +llvm::json::Object LoadInfoReport::emitInputEntry(Input *I, bool IncludeIndex) { + llvm::json::Object Entry; + if (IncludeIndex) + Entry["Index"] = I->getInputOrdinal() - Input::getInternalInputOrderEnd(); + Entry["Name"] = I->getFileName(); + Entry["ResolvedPath"] = I->getResolvedPath().native(); + + InputFile *IF = I->getInputFile(); + assert(IF && "InputFile should not be null for user inputs"); + + Entry["Type"] = getTypeName(IF->getKind()).str(); + + if (IF->isDynamicLibrary()) { + auto *DynObj = llvm::cast(IF); + Entry["SOName"] = DynObj->getSOName(); + Entry["Needed"] = DynObj->isELFNeeded(); + } + + if (IF->isArchive()) { + auto *AF = llvm::cast(IF); + emitArchiveMembers(Entry, AF); + } + + if (IF->isLinkerScript()) { + auto *LSF = llvm::cast(IF); + emitNestedInputs(Entry, LSF); + } + + return Entry; +} + +bool LoadInfoReport::emitLoadInfoJson(llvm::StringRef Filename) { + std::error_code EC; + llvm::raw_fd_ostream OS(Filename, EC); + if (EC) { + DiagEngine.raise(Diag::unable_to_write_json_file) + << Filename << EC.message(); + return false; + } + + llvm::json::Array Inputs; + + for (InputAction *Action : Actions) { + InputAction::InputActionKind Kind = Action->getInputActionKind(); + + if (isBehaviorOption(Kind)) { + llvm::StringRef OptName = getBehaviorOptionName(Kind); + if (!OptName.empty()) { + llvm::json::Object BehaviorEntry; + BehaviorEntry["InputBehaviorOption"] = OptName.str(); + Inputs.push_back(std::move(BehaviorEntry)); + } + continue; + } + + Input *I = Action->getInput(); + // DefSymAction has no associated Input. + if (!I) + continue; + + InputFile *IF = I->getInputFile(); + assert(IF && "InputFile should not be null for user inputs"); + if (IF->isInternal()) + continue; + + Inputs.push_back(emitInputEntry(I, /*IncludeIndex=*/true)); + } + + llvm::json::Object Root; + Root["LinkerRevision"] = eld::getELDRevision().str(); + Root["Inputs"] = std::move(Inputs); + + OS << llvm::formatv("{0:2}\n", llvm::json::Value(std::move(Root))); + return true; +} diff --git a/test/Common/standalone/EmitLoadInfoJson/EmitLoadInfoJson.test b/test/Common/standalone/EmitLoadInfoJson/EmitLoadInfoJson.test new file mode 100644 index 000000000..1ea448978 --- /dev/null +++ b/test/Common/standalone/EmitLoadInfoJson/EmitLoadInfoJson.test @@ -0,0 +1,43 @@ +#---EmitLoadInfoJson.test---Executable --------------------# +#BEGIN_COMMENT +# This test checks that --emit-load-info-json emits a JSON file with +# information about all input files including objects, archives, shared +# libraries, and behavior options. +#END_COMMENT +#START_TEST +RUN: %clang %clangopts -c %p/Inputs/main.c -o %t.main.o +RUN: %clang %clangopts -c %p/Inputs/foo.c -o %t.foo.o +RUN: %clang %clangopts -c %p/Inputs/bar.c -o %t.bar.o +RUN: %clang %clangopts -c %p/Inputs/lib.c -fPIC -o %t.lib.o +RUN: %ar -cr %t.libfoobar.a %t.foo.o %t.bar.o +RUN: %link %linkopts -shared -o %t.libshared.so %t.lib.o +RUN: %link %linkopts -o %t.out %t.main.o --whole-archive %t.libfoobar.a --no-whole-archive --as-needed %t.libshared.so --no-as-needed --emit-load-info-json %t.loadinfo.json +RUN: %filecheck %s --check-prefix=JSON < %t.loadinfo.json +#END_TEST + +JSON-DAG: "LinkerRevision" +JSON-DAG: "Inputs" + +JSON-DAG: "Index": +JSON-DAG: "Name": "{{.*}}main.o" +JSON-DAG: "Type": "Object" +JSON-DAG: "ResolvedPath": "{{.*}}main.o" + +JSON-DAG: "InputBehaviorOption": "--whole-archive" + +JSON-DAG: "Name": "{{.*}}libfoobar.a" +JSON-DAG: "Type": "Archive" +JSON-DAG: "IncludedCount": +JSON-DAG: "TotalCount": +JSON-DAG: "Members" +JSON-DAG: "Reason": "WholeArchive" + +JSON-DAG: "InputBehaviorOption": "--no-whole-archive" +JSON-DAG: "InputBehaviorOption": "--as-needed" + +JSON-DAG: "Name": "{{.*}}libshared.so" +JSON-DAG: "Type": "SharedLibrary" +JSON-DAG: "SOName": +JSON-DAG: "Needed": + +JSON-DAG: "InputBehaviorOption": "--no-as-needed" diff --git a/test/Common/standalone/EmitLoadInfoJson/Inputs/bar.c b/test/Common/standalone/EmitLoadInfoJson/Inputs/bar.c new file mode 100644 index 000000000..6119e51bd --- /dev/null +++ b/test/Common/standalone/EmitLoadInfoJson/Inputs/bar.c @@ -0,0 +1 @@ +int bar(void) { return 1; } diff --git a/test/Common/standalone/EmitLoadInfoJson/Inputs/foo.c b/test/Common/standalone/EmitLoadInfoJson/Inputs/foo.c new file mode 100644 index 000000000..08649ae26 --- /dev/null +++ b/test/Common/standalone/EmitLoadInfoJson/Inputs/foo.c @@ -0,0 +1,3 @@ +extern int bar(void); + +int foo(void) { return bar(); } diff --git a/test/Common/standalone/EmitLoadInfoJson/Inputs/lib.c b/test/Common/standalone/EmitLoadInfoJson/Inputs/lib.c new file mode 100644 index 000000000..e4fbccc45 --- /dev/null +++ b/test/Common/standalone/EmitLoadInfoJson/Inputs/lib.c @@ -0,0 +1 @@ +int libfunc(void) { return 42; } diff --git a/test/Common/standalone/EmitLoadInfoJson/Inputs/main.c b/test/Common/standalone/EmitLoadInfoJson/Inputs/main.c new file mode 100644 index 000000000..a5c02580e --- /dev/null +++ b/test/Common/standalone/EmitLoadInfoJson/Inputs/main.c @@ -0,0 +1,3 @@ +extern int foo(void); + +int main(void) { return foo(); }