diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp
--- a/lld/ELF/Arch/PPC64.cpp
+++ b/lld/ELF/Arch/PPC64.cpp
@@ -6,11 +6,13 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "SymbolTable.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
 #include "Thunks.h"
 #include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
 #include "llvm/Support/Endian.h"
 
 using namespace llvm;
@@ -104,6 +106,85 @@
   return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS;
 }
 
+static bool addOptional(StringRef name, uint64_t value,
+                        std::vector<Defined *> &defined) {
+  Symbol *sym = symtab->find(name);
+  if (!sym || sym->isDefined())
+    return false;
+  sym->resolve(Defined{/*file=*/nullptr, saver.save(name), STB_GLOBAL,
+                       STV_HIDDEN, STT_FUNC, value,
+                       /*size=*/0, /*section=*/nullptr});
+  defined.push_back(cast<Defined>(sym));
+  return true;
+}
+
+// If from is 14, write ${prefix}14: firstInsn; ${prefix}15:
+// firstInsn+0x200008; ...; ${prefix}31: firstInsn+(31-14)*0x200008; $tail
+// The labels are defined only if they exist in the symbol table.
+static void writeSequence(uint32_t *begin, uint32_t *end, const char *prefix,
+                          int from, uint32_t firstInsn,
+                          ArrayRef<uint32_t> tail) {
+  std::vector<Defined *> defined;
+  char name[16];
+  uint32_t *first;
+  uint32_t *buf = begin;
+  for (int r = from; r < 32; ++r) {
+    format("%s%d", prefix, r).snprint(name, sizeof(name));
+    if (addOptional(name, 4 * (r - 14), defined) && defined.size() == 1)
+      first = buf;
+    write32(buf++, firstInsn + 0x200008 * (r - 14));
+  }
+  for (uint32_t insn : tail)
+    write32(buf++, insn);
+
+  if (defined.empty())
+    return;
+  // The full section content has the extent of [begin, end). We drop unused
+  // instructions and write [first,end).
+  auto *f = reinterpret_cast<uint8_t *>(first);
+  auto *sec = make<InputSection>(
+      nullptr, SHF_ALLOC, SHT_PROGBITS, 4,
+      makeArrayRef(f, reinterpret_cast<uint8_t *>(end) - f), ".text");
+  inputSections.push_back(sec);
+  for (Defined *sym : defined) {
+    sym->section = sec;
+    sym->value -= 4 * (first - begin);
+  }
+}
+
+// Implements some save and restore functions as described by ELF V2 ABI to be
+// compatible with GCC. With GCC -Os, when the number of call-saved registers
+// exceeds a certain threshold, GCC generates _savegpr0_* _restgpr0_* calls and
+// expects the linker to define them. See
+// https://sourceware.org/pipermail/binutils/2002-February/017444.html and
+// https://sourceware.org/pipermail/binutils/2004-August/036765.html . This is
+// weird because libgcc.a would be the natural place. The linker generation
+// approach has the advantage that the linker can generate multiple copies to
+// avoid long branch thunks. However, we don't consider the advantage
+// significant enough to complicate our trunk implementation, so we take the
+// simple approach and synthesize .text sections providing the implementation.
+void elf::addPPC64SaveRestore() {
+  static uint32_t savegpr0[20], restgpr0[21], savegpr1[19], restgpr1[19];
+  constexpr uint32_t blr = 0x4e800020, mtlr_0 = 0x7c0803a6;
+
+  // _restgpr0_14: ld 14, -144(1); _restgpr0_15: ld 15, -136(1); ...
+  // Tail: ld 0, 16(1); mtlr 0; blr
+  writeSequence(restgpr0, std::end(restgpr0), "_restgpr0_", 14, 0xe9c1ff70,
+                {0xe8010010, mtlr_0, blr});
+  // _restgpr1_14: ld 14, -144(12); _restgpr1_15: ld 15, -136(12); ...
+  // Tail: blr
+  writeSequence(restgpr1, std::end(restgpr1), "_restgpr1_", 14, 0xe9ccff70,
+                {blr});
+  // _savegpr0_14: std 14, -144(1); _savegpr0_15: std 15, -136(1); ...
+  // Tail: std 0,16(1); blr
+  writeSequence(savegpr0, std::end(savegpr0), "_savegpr0_", 14, 0xf9c1ff70,
+                {0xf8010010, blr});
+  // _savegpr1_14: std 14, -144(12); _savegpr1_15: std 15, -136(12); ...
+  // Tail: blr
+  writeSequence(savegpr1, std::end(savegpr1), "_savegpr1_", 14, 0xf9ccff70,
+                {blr});
+}
+
 // Find the R_PPC64_ADDR64 in .rela.toc with matching offset.
 template <typename ELFT>
 static std::pair<Defined *, int64_t>
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -213,6 +213,7 @@
 // the .toc section.
 bool isPPC64SmallCodeModelTocReloc(RelType type);
 
+void addPPC64SaveRestore();
 uint64_t getPPC64TocBase();
 uint64_t getAArch64Page(uint64_t expr);
 
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -264,6 +264,8 @@
     // glibc *crt1.o has a undefined reference to _SDA_BASE_. Since we don't
     // support Small Data Area, define it arbitrarily as 0.
     addOptionalRegular("_SDA_BASE_", nullptr, 0, STV_HIDDEN);
+  } else if (config->emachine == EM_PPC64) {
+    addPPC64SaveRestore();
   }
 
   // The Power Architecture 64-bit v2 ABI defines a TableOfContents (TOC) which
diff --git a/lld/test/ELF/ppc64-restgpr0.s b/lld/test/ELF/ppc64-restgpr0.s
new file mode 100644
--- /dev/null
+++ b/lld/test/ELF/ppc64-restgpr0.s
@@ -0,0 +1,38 @@
+# REQUIRES: ppc
+## Test code sequences of synthesized _restgpr0_{14..31}
+
+# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t14.o
+# RUN: ld.lld %t14.o -o %t14
+# RUN: llvm-objdump -d %t14 | FileCheck --check-prefix=R14 %s
+
+# R14-LABEL: <_restgpr0_14>:
+# R14-NEXT:    ld 14, -144(1)
+# R14-NEXT:    ld 15, -136(1)
+# R14-EMPTY:
+# R14-NEXT:  <_restgpr0_16>:
+# R14-NEXT:    ld 16, -128(1)
+# R14:         ld 31, -8(1)
+# R14-NEXT:    ld 0, 16(1)
+# R14-NEXT:    mtlr 0
+# R14-NEXT:    blr
+
+## Don't synthesize _restgpr0_{14..30} because they are unused.
+# RUN: echo 'bl _restgpr0_31' | llvm-mc -filetype=obj -triple=ppc64 - -o %t31.o
+# RUN: ld.lld %t31.o -o %t31
+# RUN: llvm-objdump -d %t31 | FileCheck --check-prefix=R31 %s
+
+# R31-LABEL: Disassembly of section .text:
+# R31-EMPTY:
+# R31-NEXT:  <_restgpr0_31>:
+# R31-NEXT:    ld 31, -8(1)
+# R31-NEXT:    ld 0, 16(1)
+# R31-NEXT:    mtlr 0
+# R31-NEXT:    blr
+
+# RUN: echo 'bl _restgpr0_32' | llvm-mc -filetype=obj -triple=ppc64 - -o %t32.o
+# RUN: not ld.lld %t32.o -o /dev/null
+
+.globl _start
+_start:
+  bl _restgpr0_14
+  bl _restgpr0_16
diff --git a/lld/test/ELF/ppc64-restgpr1.s b/lld/test/ELF/ppc64-restgpr1.s
new file mode 100644
--- /dev/null
+++ b/lld/test/ELF/ppc64-restgpr1.s
@@ -0,0 +1,34 @@
+# REQUIRES: ppc
+## Test code sequences of synthesized _restgpr1_{14..31}
+
+# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t14.o
+# RUN: ld.lld %t14.o -o %t14
+# RUN: llvm-objdump -d %t14 | FileCheck --check-prefix=R14 %s
+
+# R14:       <_restgpr1_14>:
+# R14-NEXT:    ld 14, -144(12)
+# R14-NEXT:    ld 15, -136(12)
+# R14-EMPTY:
+# R14-NEXT:  <_restgpr1_16>:
+# R14-NEXT:    ld 16, -128(12)
+# R14:         ld 31, -8(12)
+# R14-NEXT:    blr
+
+## Don't synthesize _restgpr1_{14..30} because they are unused.
+# RUN: echo 'bl _restgpr1_31' | llvm-mc -filetype=obj -triple=ppc64 - -o %t31.o
+# RUN: ld.lld %t31.o -o %t31
+# RUN: llvm-objdump -d %t31 | FileCheck --check-prefix=R31 %s
+
+# R31-LABEL: Disassembly of section .text:
+# R31-EMPTY:
+# R31-NEXT:  <_restgpr1_31>:
+# R31-NEXT:    ld 31, -8(12)
+# R31-NEXT:    blr
+
+# RUN: echo 'bl _restgpr1_32' | llvm-mc -filetype=obj -triple=ppc64le - -o %t32.o
+# RUN: not ld.lld %t32.o -o /dev/null
+
+.globl _start
+_start:
+  bl _restgpr1_14
+  bl _restgpr1_16
diff --git a/lld/test/ELF/ppc64-savegpr0.s b/lld/test/ELF/ppc64-savegpr0.s
new file mode 100644
--- /dev/null
+++ b/lld/test/ELF/ppc64-savegpr0.s
@@ -0,0 +1,36 @@
+# REQUIRES: ppc
+## Test code sequences of synthesized _savegpr0_{14..31}
+
+# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t14.o
+# RUN: ld.lld %t14.o -o %t14
+# RUN: llvm-objdump -d %t14 | FileCheck --check-prefix=R14 %s
+
+# R14-LABEL: <_savegpr0_14>:
+# R14-NEXT:    std 14, -144(1)
+# R14-NEXT:    std 15, -136(1)
+# R14-EMPTY:
+# R14-NEXT:  <_savegpr0_16>:
+# R14-NEXT:    std 16, -128(1)
+# R14:         std 31, -8(1)
+# R14-NEXT:    std 0, 16(1)
+# R14-NEXT:    blr
+
+## Don't synthesize _savegpr0_{14..30} because they are unused.
+# RUN: echo 'bl _savegpr0_31' | llvm-mc -filetype=obj -triple=ppc64 - -o %t31.o
+# RUN: ld.lld %t31.o -o %t31
+# RUN: llvm-objdump -d %t31 | FileCheck --check-prefix=R31 %s
+
+# R31-LABEL: Disassembly of section .text:
+# R31-EMPTY:
+# R31-NEXT:  <_savegpr0_31>:
+# R31-NEXT:    std 31, -8(1)
+# R31-NEXT:    std 0, 16(1)
+# R31-NEXT:    blr
+
+# RUN: echo 'bl _savegpr0_32' | llvm-mc -filetype=obj -triple=ppc64 - -o %t32.o
+# RUN: not ld.lld %t32.o -o /dev/null
+
+.globl _start
+_start:
+  bl _savegpr0_14
+  bl _savegpr0_16
diff --git a/lld/test/ELF/ppc64-savegpr1.s b/lld/test/ELF/ppc64-savegpr1.s
new file mode 100644
--- /dev/null
+++ b/lld/test/ELF/ppc64-savegpr1.s
@@ -0,0 +1,34 @@
+# REQUIRES: ppc
+## Test code sequences of synthesized _savegpr1_{14..31}
+
+# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t14.o
+# RUN: ld.lld %t14.o -o %t14
+# RUN: llvm-objdump -d %t14 | FileCheck --check-prefix=R14 %s
+
+# R14-LABEL: <_savegpr1_14>:
+# R14-NEXT:    std 14, -144(12)
+# R14-NEXT:    std 15, -136(12)
+# R14-EMPTY:
+# R14-NEXT:  <_savegpr1_16>:
+# R14-NEXT:    std 16, -128(12)
+# R14:         std 31, -8(12)
+# R14-NEXT:    blr
+
+## Don't synthesize _savegpr1_{14..30} because they are unused.
+# RUN: echo 'bl _savegpr1_31' | llvm-mc -filetype=obj -triple=ppc64 - -o %t31.o
+# RUN: ld.lld %t31.o -o %t31
+# RUN: llvm-objdump -d %t31 | FileCheck --check-prefix=R31 %s
+
+# R31-LABEL: Disassembly of section .text:
+# R31-EMPTY:
+# R31-NEXT:  <_savegpr1_31>:
+# R31-NEXT:    std 31, -8(12)
+# R31-NEXT:    blr
+
+# RUN: echo 'bl _savegpr1_32' | llvm-mc -filetype=obj -triple=ppc64le - -o %t32.o
+# RUN: not ld.lld %t32.o -o /dev/null
+
+.globl _start
+_start:
+  bl _savegpr1_14
+  bl _savegpr1_16
diff --git a/lld/test/ELF/ppc64-saveres.s b/lld/test/ELF/ppc64-saveres.s
new file mode 100644
--- /dev/null
+++ b/lld/test/ELF/ppc64-saveres.s
@@ -0,0 +1,31 @@
+# REQUIRES: ppc
+## Test that some save and restore functions can be synthesized.
+## The code sequences are tested by ppc64-restgpr*.s and ppc64-savegpr*.s
+
+# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t.o
+# RUN: ld.lld -shared %t.o -o %t.so
+# RUN: llvm-readelf -s %t.so | FileCheck --check-prefix=NM %s
+# RUN: llvm-objdump -d %t.so | FileCheck %s
+
+## The synthesized symbols are not exported.
+# NM:      FUNC LOCAL HIDDEN {{.*}} _restgpr0_30
+# NM-NEXT: FUNC LOCAL HIDDEN {{.*}} _restgpr1_30
+# NM-NEXT: FUNC LOCAL HIDDEN {{.*}} _savegpr0_30
+# NM-NEXT: FUNC LOCAL HIDDEN {{.*}} _savegpr1_30
+
+# CHECK: 00000000000[[#%x,RESTGPR0:]] <_restgpr0_30>:
+# CHECK: 00000000000[[#%x,RESTGPR1:]] <_restgpr1_30>:
+# CHECK: 00000000000[[#%x,SAVEGPR0:]] <_savegpr0_30>:
+# CHECK: 00000000000[[#%x,SAVEGPR1:]] <_savegpr1_30>:
+# CHECK-LABEL: <_start>:
+# CHECK-NEXT:    bl 0x[[#RESTGPR0]]
+# CHECK-NEXT:    bl 0x[[#RESTGPR1]]
+# CHECK-NEXT:    bl 0x[[#SAVEGPR0]]
+# CHECK-NEXT:    bl 0x[[#SAVEGPR1]]
+
+.globl _start
+_start:
+  bl _restgpr0_30
+  bl _restgpr1_30
+  bl _savegpr0_30
+  bl _savegpr1_30