diff --git a/Help/command/target_link_libraries.rst b/Help/command/target_link_libraries.rst
index 872e264e99a79c0ddeef905e0ce3788c70f4da2d..ac231bc8f2b36c870fe4a264116126d73f42c4a9 100644
--- a/Help/command/target_link_libraries.rst
+++ b/Help/command/target_link_libraries.rst
@@ -289,6 +289,91 @@ treated as :ref:`Interface Libraries`, but when they appear in
 a target's :prop_tgt:`LINK_LIBRARIES` property their object files
 will be included in the link too.
 
+.. _`Linking Object Libraries via $<TARGET_OBJECTS>`:
+
+Linking Object Libraries via $<TARGET_OBJECTS>
+""""""""""""""""""""""""""""""""""""""""""""""
+
+.. versionadded:: 3.21
+
+The object files associated with an object library may be referenced
+by the :genex:`$<TARGET_OBJECTS>` generator expression.  Such object
+files are placed on the link line *before* all libraries, regardless
+of their relative order.  Additionally, an ordering dependency will be
+added to the build sysstem to make sure the object library is up-to-date
+before the dependent target links.  For example, the code
+
+.. code-block:: cmake
+
+  add_library(obj3 OBJECT obj3.c)
+  target_compile_definitions(obj3 PUBLIC OBJ3)
+
+  add_executable(main3 main3.c)
+  target_link_libraries(main3 PRIVATE a3 $<TARGET_OBJECTS:obj3> b3)
+
+links executable ``main3`` with object files from ``main3.c``
+and ``obj3.c`` followed by the ``a3`` and ``b3`` libraries.
+``main3.c`` is *not* compiled with usage requirements from ``obj3``,
+such as ``-DOBJ3``.
+
+This approach can be used to achieve transitive inclusion of object
+files in link lines as usage requirements.  Continuing the above
+example, the code
+
+.. code-block:: cmake
+
+  add_library(iface_obj3 INTERFACE)
+  target_link_libraries(iface_obj3 INTERFACE obj3 $<TARGET_OBJECTS:obj3>)
+
+creates an interface library ``iface_obj3`` that forwards the ``obj3``
+usage requirements and adds the ``obj3`` object files to dependents'
+link lines.  The code
+
+.. code-block:: cmake
+
+  add_executable(use_obj3 use_obj3.c)
+  target_link_libraries(use_obj3 PRIVATE iface_obj3)
+
+compiles ``use_obj3.c`` with ``-DOBJ3`` and links executable ``use_obj3``
+with object files from ``use_obj3.c`` and ``obj3.c``.
+
+This also works transitively through a static library.  Since a static
+library does not link, it does not consume the object files from
+object libraries referenced this way.  Instead, the object files
+become transitive link dependencies of the static library.
+Continuing the above example, the code
+
+.. code-block:: cmake
+
+  add_library(static3 STATIC static3.c)
+  target_link_libraries(static3 PRIVATE iface_obj3)
+
+  add_executable(use_static3 use_static3.c)
+  target_link_libraries(use_static3 PRIVATE static3)
+
+compiles ``static3.c`` with ``-DOBJ3`` and creates ``libstatic3.a``
+using only its own object file.  ``use_static3.c`` is compiled *without*
+``-DOBJ3`` because the usage requirement is not transitive through
+the private dependency of ``static3``.  However, the link dependencies
+of ``static3`` are propagated, including the ``iface_obj3`` reference
+to ``$<TARGET_OBJECTS:obj3>``.  The ``use_static3`` executable is
+created with object files from ``use_static3.c`` and ``obj3.c``, and
+linked to library ``libstatic3.a``.
+
+When using this approach, it is the project's responsibility to avoid
+linking multiple dependent binaries to ``iface_obj3``, because they will
+all get the ``obj3`` object files on their link lines.
+
+.. note::
+
+  Referencing :genex:`$<TARGET_OBJECTS>` in ``target_link_libraries``
+  calls worked in versions of CMake prior to 3.21 for some cases,
+  but was not fully supported:
+
+  * It did not place the object files before libraries on link lines.
+  * It did not add an ordering dependency on the object library.
+  * It did not work in Xcode with multiple architectures.
+
 Cyclic Dependencies of Static Libraries
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/Help/release/dev/link-objects-first.rst b/Help/release/dev/link-objects-first.rst
new file mode 100644
index 0000000000000000000000000000000000000000..0c622e7ae1992b3a9ba9b981609bd4eb8b8dd76e
--- /dev/null
+++ b/Help/release/dev/link-objects-first.rst
@@ -0,0 +1,8 @@
+link-objects-first
+------------------
+
+* :command:`target_link_libraries` calls referencing object libraries
+  via the :genex:`TARGET_OBJECTS` generator expression now place the
+  object files before all libraries on the link line, regardless of
+  their specified order.  See documentation on
+  :ref:`Linking Object Libraries via \$\<TARGET_OBJECTS\>` for details.
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index 5341e8d8c11c40f68f302ecb646895047c8a1a51..4a6518fde4a52c7ee72d23008d03ae9fb234004d 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -263,6 +263,12 @@ cmComputeLinkDepends::Compute()
       this->FinalLinkEntries.push_back(e);
     }
   }
+  // Place explicitly linked object files in the front.  The linker will
+  // always use them anyway, and they may depend on symbols from libraries.
+  // Append in reverse order since we reverse the final order below.
+  for (int i : cmReverseRange(this->ObjectEntries)) {
+    this->FinalLinkEntries.emplace_back(this->EntryList[i]);
+  }
   // Reverse the resulting order since we iterated in reverse.
   std::reverse(this->FinalLinkEntries.begin(), this->FinalLinkEntries.end());
 
@@ -328,6 +334,27 @@ int cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
   return index;
 }
 
+void cmComputeLinkDepends::AddLinkObject(cmLinkItem const& item)
+{
+  // Check if the item entry has already been added.
+  auto lei = this->LinkEntryIndex.find(item);
+  if (lei != this->LinkEntryIndex.end()) {
+    return;
+  }
+
+  // Allocate a spot for the item entry.
+  lei = this->AllocateLinkEntry(item);
+
+  // Initialize the item entry.
+  int index = lei->second;
+  LinkEntry& entry = this->EntryList[index];
+  entry.Item = BT<std::string>(item.AsStr(), item.Backtrace);
+  entry.IsObject = true;
+
+  // Record explicitly linked object files separately.
+  this->ObjectEntries.emplace_back(index);
+}
+
 void cmComputeLinkDepends::FollowLinkEntry(BFSEntry qe)
 {
   // Get this entry representation.
@@ -343,6 +370,7 @@ void cmComputeLinkDepends::FollowLinkEntry(BFSEntry qe)
         entry.Target->GetType() == cmStateEnums::INTERFACE_LIBRARY;
       // This target provides its own link interface information.
       this->AddLinkEntries(depender_index, iface->Libraries);
+      this->AddLinkObjects(iface->Objects);
 
       if (isIface) {
         return;
@@ -487,6 +515,7 @@ void cmComputeLinkDepends::AddDirectLinkEntries()
   cmLinkImplementation const* impl =
     this->Target->GetLinkImplementation(this->Config);
   this->AddLinkEntries(-1, impl->Libraries);
+  this->AddLinkObjects(impl->Objects);
   for (cmLinkItem const& wi : impl->WrongConfigLibraries) {
     this->CheckWrongConfigItem(wi);
   }
@@ -546,6 +575,13 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
   }
 }
 
+void cmComputeLinkDepends::AddLinkObjects(std::vector<cmLinkItem> const& objs)
+{
+  for (cmLinkItem const& obj : objs) {
+    this->AddLinkObject(obj);
+  }
+}
+
 cmLinkItem cmComputeLinkDepends::ResolveLinkItem(int depender_index,
                                                  const std::string& name)
 {
diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h
index 00fbd59729980dffa2f35366ae7a42e7b9219db2..72316f1aac667a0b873479d69b53306ffeb1d4c4 100644
--- a/Source/cmComputeLinkDepends.h
+++ b/Source/cmComputeLinkDepends.h
@@ -66,10 +66,12 @@ private:
   std::map<cmLinkItem, int>::iterator AllocateLinkEntry(
     cmLinkItem const& item);
   int AddLinkEntry(cmLinkItem const& item);
+  void AddLinkObject(cmLinkItem const& item);
   void AddVarLinkEntries(int depender_index, const char* value);
   void AddDirectLinkEntries();
   template <typename T>
   void AddLinkEntries(int depender_index, std::vector<T> const& libs);
+  void AddLinkObjects(std::vector<cmLinkItem> const& objs);
   cmLinkItem ResolveLinkItem(int depender_index, const std::string& name);
 
   // One entry for each unique item.
@@ -154,6 +156,9 @@ private:
   std::set<cmGeneratorTarget const*> OldWrongConfigItems;
   void CheckWrongConfigItem(cmLinkItem const& item);
 
+  // Record of explicitly linked object files.
+  std::vector<int> ObjectEntries;
+
   int ComponentOrderId;
   cmTargetLinkLibraryType LinkType;
   bool HasConfig;
diff --git a/Source/cmComputeTargetDepends.cxx b/Source/cmComputeTargetDepends.cxx
index 0f234170f849d34aeef2914070a3c1f2cf995828..76712f4eb18acb9221471440b35051fbdc382f20 100644
--- a/Source/cmComputeTargetDepends.cxx
+++ b/Source/cmComputeTargetDepends.cxx
@@ -20,6 +20,7 @@
 #include "cmProperty.h"
 #include "cmRange.h"
 #include "cmSourceFile.h"
+#include "cmSourceFileLocationKind.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
@@ -227,6 +228,12 @@ void cmComputeTargetDepends::CollectTargetDepends(int depender_index)
             this->AddInterfaceDepends(depender_index, lib, it, emitted);
           }
         }
+        for (cmLinkItem const& obj : impl->Objects) {
+          if (cmSourceFile const* o = depender->Makefile->GetSource(
+                obj.AsStr(), cmSourceFileLocationKind::Known)) {
+            this->AddObjectDepends(depender_index, o, emitted);
+          }
+        }
       }
 
       // Add dependencies on object libraries not otherwise handled above.
@@ -274,6 +281,12 @@ void cmComputeTargetDepends::AddInterfaceDepends(
         this->AddInterfaceDepends(depender_index, libBT, config, emitted);
       }
     }
+    for (cmLinkItem const& obj : iface->Objects) {
+      if (cmSourceFile const* o = depender->Makefile->GetSource(
+            obj.AsStr(), cmSourceFileLocationKind::Known)) {
+        this->AddObjectDepends(depender_index, o, emitted);
+      }
+    }
   }
 }
 
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 51742cf67f9c3cfd7f7e21cc788b5508412a7ee6..5deb2dfe45ca7eef1018262e850f77a9c80b4a5a 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -6275,8 +6275,8 @@ cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem(
 void cmGeneratorTarget::ExpandLinkItems(
   std::string const& prop, std::string const& value, std::string const& config,
   cmGeneratorTarget const* headTarget, bool usage_requirements_only,
-  std::vector<cmLinkItem>& items, bool& hadHeadSensitiveCondition,
-  bool& hadContextSensitiveCondition,
+  std::vector<cmLinkItem>& items, std::vector<cmLinkItem>& objects,
+  bool& hadHeadSensitiveCondition, bool& hadContextSensitiveCondition,
   bool& hadLinkLanguageSensitiveCondition) const
 {
   // Keep this logic in sync with ComputeLinkImplementationLibraries.
@@ -6294,9 +6294,22 @@ void cmGeneratorTarget::ExpandLinkItems(
   cmExpandList(cge->Evaluate(this->LocalGenerator, config, headTarget,
                              &dagChecker, this, headTarget->LinkerLanguage),
                libs);
+  cmMakefile const* mf = this->LocalGenerator->GetMakefile();
   for (std::string const& lib : libs) {
     if (cm::optional<cmLinkItem> maybeItem =
           this->LookupLinkItem(lib, cge->GetBacktrace())) {
+      if (!maybeItem->Target) {
+        // Report explicitly linked object files separately.
+        std::string const& maybeObj = maybeItem->AsStr();
+        if (cmSystemTools::FileIsFullPath(maybeObj)) {
+          cmSourceFile const* sf =
+            mf->GetSource(maybeObj, cmSourceFileLocationKind::Known);
+          if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
+            objects.emplace_back(std::move(*maybeItem));
+            continue;
+          }
+        }
+      }
       items.emplace_back(std::move(*maybeItem));
     }
   }
@@ -6804,7 +6817,7 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
     // The interface libraries have been explicitly set.
     this->ExpandLinkItems(linkIfaceProp, *explicitLibraries, config,
                           headTarget, usage_requirements_only, iface.Libraries,
-                          iface.HadHeadSensitiveCondition,
+                          iface.Objects, iface.HadHeadSensitiveCondition,
                           iface.HadContextSensitiveCondition,
                           iface.HadLinkLanguageSensitiveCondition);
     return;
@@ -6828,6 +6841,7 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
       // Compare the link implementation fallback link interface to the
       // preferred new link interface property and warn if different.
       std::vector<cmLinkItem> ifaceLibs;
+      std::vector<cmLinkItem> ifaceObjects;
       static const std::string newProp = "INTERFACE_LINK_LIBRARIES";
       if (cmProp newExplicitLibraries = this->GetProperty(newProp)) {
         bool hadHeadSensitiveConditionDummy = false;
@@ -6835,7 +6849,7 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
         bool hadLinkLanguageSensitiveConditionDummy = false;
         this->ExpandLinkItems(newProp, *newExplicitLibraries, config,
                               headTarget, usage_requirements_only, ifaceLibs,
-                              hadHeadSensitiveConditionDummy,
+                              ifaceObjects, hadHeadSensitiveConditionDummy,
                               hadContextSensitiveConditionDummy,
                               hadLinkLanguageSensitiveConditionDummy);
       }
@@ -6903,7 +6917,7 @@ const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface(
     cmExpandList(info->Languages, iface.Languages);
     this->ExpandLinkItems(info->LibrariesProp, info->Libraries, config,
                           headTarget, usage_requirements_only, iface.Libraries,
-                          iface.HadHeadSensitiveCondition,
+                          iface.Objects, iface.HadHeadSensitiveCondition,
                           iface.HadContextSensitiveCondition,
                           iface.HadLinkLanguageSensitiveCondition);
     std::vector<std::string> deps = cmExpandedList(info->SharedDeps);
@@ -7425,6 +7439,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
   cmGeneratorTarget const* head) const
 {
   cmLocalGenerator const* lg = this->LocalGenerator;
+  cmMakefile const* mf = lg->GetMakefile();
   cmStringRange entryRange = this->Target->GetLinkImplementationEntries();
   cmBacktraceRange btRange = this->Target->GetLinkImplementationBacktraces();
   cmBacktraceRange::const_iterator btIt = btRange.begin();
@@ -7500,6 +7515,19 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
 
       // The entry is meant for this configuration.
       cmLinkItem item = this->ResolveLinkItem(name, *btIt, lg);
+      if (!item.Target) {
+        // Report explicitly linked object files separately.
+        std::string const& maybeObj = item.AsStr();
+        if (cmSystemTools::FileIsFullPath(maybeObj)) {
+          cmSourceFile const* sf =
+            mf->GetSource(maybeObj, cmSourceFileLocationKind::Known);
+          if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
+            impl.Objects.emplace_back(std::move(item));
+            continue;
+          }
+        }
+      }
+
       impl.Libraries.emplace_back(std::move(item), evaluated != *le);
     }
 
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index dbc47b8f7e1d414691e2194d94593007e7c88d0e..be36f3f206eb1087b38cb595d9ab91ff8be78fd5 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -1036,6 +1036,7 @@ private:
                        const cmGeneratorTarget* headTarget,
                        bool usage_requirements_only,
                        std::vector<cmLinkItem>& items,
+                       std::vector<cmLinkItem>& objects,
                        bool& hadHeadSensitiveCondition,
                        bool& hadContextSensitiveCondition,
                        bool& hadLinkLanguageSensitiveCondition) const;
diff --git a/Source/cmLinkItem.h b/Source/cmLinkItem.h
index 5a90e7ec65cf23cb0239b62b8dffa0d0af2e8437..db44938a377800fbc3f832cbc8db1cbee850d1f2 100644
--- a/Source/cmLinkItem.h
+++ b/Source/cmLinkItem.h
@@ -50,6 +50,9 @@ struct cmLinkImplementationLibraries
   // Libraries linked directly in this configuration.
   std::vector<cmLinkImplItem> Libraries;
 
+  // Object files linked directly in this configuration.
+  std::vector<cmLinkItem> Objects;
+
   // Libraries linked directly in other configurations.
   // Needed only for OLD behavior of CMP0003.
   std::vector<cmLinkItem> WrongConfigLibraries;
@@ -63,6 +66,9 @@ struct cmLinkInterfaceLibraries
   // Libraries listed in the interface.
   std::vector<cmLinkItem> Libraries;
 
+  // Object files listed in the interface.
+  std::vector<cmLinkItem> Objects;
+
   // Whether the list depends on a genex referencing the head target.
   bool HadHeadSensitiveCondition = false;
 
diff --git a/Tests/ObjectLibrary/CMakeLists.txt b/Tests/ObjectLibrary/CMakeLists.txt
index 74f34e4d02d735d4604684118b03f15918f68734..06167a83f4c272bd85a1be75812b2644d0329759 100644
--- a/Tests/ObjectLibrary/CMakeLists.txt
+++ b/Tests/ObjectLibrary/CMakeLists.txt
@@ -74,4 +74,6 @@ target_link_libraries(UseABstaticObjs ABstatic)
 
 add_subdirectory(ExportLanguages)
 
+add_subdirectory(LinkObjects)
+
 add_subdirectory(Transitive)
diff --git a/Tests/ObjectLibrary/LinkObjects/CMakeLists.txt b/Tests/ObjectLibrary/LinkObjects/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..899be451845897ff432bd98cb12abc43f140565f
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/CMakeLists.txt
@@ -0,0 +1,45 @@
+add_executable(LinkObjects main.c)
+
+# Link TARGET_OBJECTS through LINK_LIBRARIES.
+add_library(LinkObjectsAObj OBJECT a_obj.c)
+add_library(LinkObjectsADep STATIC a_dep.c)
+target_compile_definitions(LinkObjectsAObj PUBLIC OBJA)
+target_link_libraries(LinkObjects PRIVATE LinkObjectsADep $<TARGET_OBJECTS:LinkObjectsAObj>)
+
+# Link TARGET_OBJECTS through INTERFACE_LINK_LIBRARIES with usage requirements.
+add_library(LinkObjectsB INTERFACE)
+add_library(LinkObjectsBObj OBJECT b_obj.c)
+add_library(LinkObjectsBDep STATIC b_dep.c)
+target_compile_definitions(LinkObjectsBObj PUBLIC OBJB)
+target_link_libraries(LinkObjectsB INTERFACE LinkObjectsBObj $<TARGET_OBJECTS:LinkObjectsBObj>)
+target_link_libraries(LinkObjectsBObj PRIVATE LinkObjectsBDep)
+target_link_libraries(LinkObjects PRIVATE LinkObjectsB)
+
+# Link TARGET_OBJECTS through INTERFACE_LINK_LIBRARIES without usage requirements.
+add_library(LinkObjectsC INTERFACE)
+add_library(LinkObjectsCObj OBJECT c_obj.c)
+add_library(LinkObjectsCDep STATIC c_dep.c)
+target_compile_definitions(LinkObjectsCObj PUBLIC OBJC)
+target_link_libraries(LinkObjectsC INTERFACE LinkObjectsCDep $<TARGET_OBJECTS:LinkObjectsCObj>)
+target_link_libraries(LinkObjectsCObj PRIVATE LinkObjectsCDep)
+target_link_libraries(LinkObjects PRIVATE LinkObjectsC)
+
+# Link TARGET_OBJECTS through both LINK_LIBRARIES and INTERFACE_LINK_LIBRARIES, deduplicated.
+add_library(LinkObjectsD INTERFACE)
+add_library(LinkObjectsDObj OBJECT d_obj.c)
+add_library(LinkObjectsDDep STATIC d_dep.c)
+target_compile_definitions(LinkObjectsDObj PUBLIC OBJD)
+target_link_libraries(LinkObjectsD INTERFACE LinkObjectsDObj $<TARGET_OBJECTS:LinkObjectsDObj>)
+target_link_libraries(LinkObjectsDObj PRIVATE LinkObjectsDDep)
+target_link_libraries(LinkObjects PRIVATE $<TARGET_OBJECTS:LinkObjectsDObj> LinkObjectsD)
+
+# Link TARGET_OBJECTS through STATIC library private dependency.
+add_library(LinkObjectsE INTERFACE)
+add_library(LinkObjectsEObj OBJECT e_obj.c)
+add_library(LinkObjectsEDep STATIC e_dep.c)
+add_library(LinkObjectsEStatic STATIC e_lib.c)
+target_compile_definitions(LinkObjectsEObj PUBLIC OBJE)
+target_link_libraries(LinkObjectsE INTERFACE LinkObjectsEObj $<TARGET_OBJECTS:LinkObjectsEObj>)
+target_link_libraries(LinkObjectsEObj PRIVATE LinkObjectsEDep)
+target_link_libraries(LinkObjectsEStatic PRIVATE LinkObjectsE)
+target_link_libraries(LinkObjects PRIVATE LinkObjectsEStatic)
diff --git a/Tests/ObjectLibrary/LinkObjects/a_dep.c b/Tests/ObjectLibrary/LinkObjects/a_dep.c
new file mode 100644
index 0000000000000000000000000000000000000000..01287405c86f78cd791b718669df93a9aae48079
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/a_dep.c
@@ -0,0 +1,7 @@
+#ifdef OBJA
+#  error "OBJA is defined, but should not be"
+#endif
+int a_dep(void)
+{
+  return 0;
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/a_obj.c b/Tests/ObjectLibrary/LinkObjects/a_obj.c
new file mode 100644
index 0000000000000000000000000000000000000000..5e79c60c5ac81e912c6f614f1f007dcfc97ace74
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/a_obj.c
@@ -0,0 +1,8 @@
+#ifndef OBJA
+#  error "OBJA is not defined, but should be"
+#endif
+extern int a_dep(void);
+int a_obj(void)
+{
+  return a_dep();
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/b_dep.c b/Tests/ObjectLibrary/LinkObjects/b_dep.c
new file mode 100644
index 0000000000000000000000000000000000000000..ad5d36737ee746b5881623dc4782aaef8b8aecd8
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/b_dep.c
@@ -0,0 +1,7 @@
+#ifdef OBJB
+#  error "OBJB is defined, but should not be"
+#endif
+int b_dep(void)
+{
+  return 0;
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/b_obj.c b/Tests/ObjectLibrary/LinkObjects/b_obj.c
new file mode 100644
index 0000000000000000000000000000000000000000..d0f426a241a4a600cf1c494e9554b7a05d65f17b
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/b_obj.c
@@ -0,0 +1,8 @@
+#ifndef OBJB
+#  error "OBJB is not defined, but should be"
+#endif
+extern int b_dep(void);
+int b_obj(void)
+{
+  return b_dep();
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/c_dep.c b/Tests/ObjectLibrary/LinkObjects/c_dep.c
new file mode 100644
index 0000000000000000000000000000000000000000..1d99ab874e4a3d584879419a7064e6213363ca63
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/c_dep.c
@@ -0,0 +1,7 @@
+#ifdef OBJC
+#  error "OBJC is defined, but should not be"
+#endif
+int c_dep(void)
+{
+  return 0;
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/c_obj.c b/Tests/ObjectLibrary/LinkObjects/c_obj.c
new file mode 100644
index 0000000000000000000000000000000000000000..08fa5f5bdbf65493e1bce0bcae38c1f826093c73
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/c_obj.c
@@ -0,0 +1,8 @@
+#ifndef OBJC
+#  error "OBJC is not defined, but should be"
+#endif
+extern int c_dep(void);
+int c_obj(void)
+{
+  return c_dep();
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/d_dep.c b/Tests/ObjectLibrary/LinkObjects/d_dep.c
new file mode 100644
index 0000000000000000000000000000000000000000..cf09314115c71e3041b817e4e7b3e2e7b2bd6314
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/d_dep.c
@@ -0,0 +1,7 @@
+#ifdef OBJD
+#  error "OBJD is defined, but should not be"
+#endif
+int d_dep(void)
+{
+  return 0;
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/d_obj.c b/Tests/ObjectLibrary/LinkObjects/d_obj.c
new file mode 100644
index 0000000000000000000000000000000000000000..d14ce660ebe57afd8cb7bf8e171e0bfc949d5d50
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/d_obj.c
@@ -0,0 +1,8 @@
+#ifndef OBJD
+#  error "OBJD is not defined, but should be"
+#endif
+extern int d_dep(void);
+int d_obj(void)
+{
+  return d_dep();
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/e_dep.c b/Tests/ObjectLibrary/LinkObjects/e_dep.c
new file mode 100644
index 0000000000000000000000000000000000000000..7fb70c10ad224f833319a5fedce6c9836cb38abb
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/e_dep.c
@@ -0,0 +1,7 @@
+#ifdef OBJE
+#  error "OBJE is defined, but should not be"
+#endif
+int e_dep(void)
+{
+  return 0;
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/e_lib.c b/Tests/ObjectLibrary/LinkObjects/e_lib.c
new file mode 100644
index 0000000000000000000000000000000000000000..9bb3a44355c6a2093af0a638e58fa484560b1b1a
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/e_lib.c
@@ -0,0 +1,5 @@
+extern int e_obj(void);
+int e_lib(void)
+{
+  return e_obj();
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/e_obj.c b/Tests/ObjectLibrary/LinkObjects/e_obj.c
new file mode 100644
index 0000000000000000000000000000000000000000..02624ebfc2c363932db49296ebe085c3888ec8a0
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/e_obj.c
@@ -0,0 +1,8 @@
+#ifndef OBJE
+#  error "OBJE is not defined, but should be"
+#endif
+extern int e_dep(void);
+int e_obj(void)
+{
+  return e_dep();
+}
diff --git a/Tests/ObjectLibrary/LinkObjects/main.c b/Tests/ObjectLibrary/LinkObjects/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..c09c4f11bd4a1bc309a9ab285a196e8768f29e2f
--- /dev/null
+++ b/Tests/ObjectLibrary/LinkObjects/main.c
@@ -0,0 +1,24 @@
+#ifdef OBJA
+#  error "OBJA is defined, but should not be"
+#endif
+#ifndef OBJB
+#  error "OBJB is not defined, but should be"
+#endif
+#ifdef OBJC
+#  error "OBJC is defined, but should not be"
+#endif
+#ifndef OBJD
+#  error "OBJD is not defined, but should be"
+#endif
+#ifdef OBJE
+#  error "OBJE is defined, but should not be"
+#endif
+extern int a_obj(void);
+extern int b_obj(void);
+extern int c_obj(void);
+extern int d_obj(void);
+extern int e_lib(void);
+int main(void)
+{
+  return a_obj() + b_obj() + c_obj() + d_obj() + e_lib();
+}