Fix ODR violations in public fuzzers

In 93101066, I assumed a public_fuzzer didn't depend on private PDFium
code, however this is not true.  Some public_fuzzer's have direct
dependencies on PDFium internals.  So in component builds, they will
both statically link in parts of PDFium, and dynamically link in (the
same code from) libpdfium.so. The fix is to export the public fuzzers
from libpdfium.so too.

BUG=960288
R=thestig

Change-Id: I10d03c46c72b454760e5664c4939ab173557cb3b
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/54250
Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 0581fde..42f2093 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -46,6 +46,11 @@
   }
 }
 
+config("pdfium_implementation_config") {
+  defines = [ "FPDF_IMPLEMENTATION" ]
+  visibility = [ ":pdfium_public_headers" ]
+}
+
 config("pdfium_public_config") {
   defines = []
 
@@ -101,7 +106,7 @@
   configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
 
-jumbo_source_set("pdfium_public_headers") {
+jumbo_source_set("pdfium_public_headers_impl") {
   sources = [
     "public/cpp/fpdf_deleters.h",
     "public/cpp/fpdf_scopers.h",
@@ -125,16 +130,24 @@
     "public/fpdf_transformpage.h",
     "public/fpdfview.h",
   ]
+}
 
-  public_configs = [ ":pdfium_public_config" ]
+group("pdfium_public_headers") {
+  public_deps = [
+    ":pdfium_public_headers_impl",
+  ]
+  public_configs = [
+    ":pdfium_public_config",
+    ":pdfium_implementation_config",
+  ]
 }
 
 jumbo_component("pdfium") {
   libs = []
   configs += [ ":pdfium_core_config" ]
+  public_configs = [ ":pdfium_public_config" ]
 
   deps = [
-    ":pdfium_public_headers",
     "constants",
     "core/fpdfapi",
     "core/fpdfapi/page",
@@ -151,7 +164,7 @@
   ]
 
   public_deps = [
-    ":pdfium_public_headers",
+    ":pdfium_public_headers_impl",
     "core/fxcrt",
   ]
 
@@ -288,11 +301,11 @@
     "testing/embedder_test_main.cpp",
   ]
   deps = [
-    ":pdfium",
     "core/fpdfapi/edit:embeddertests",
     "core/fpdfapi/parser:embeddertests",
     "core/fpdfapi/render:embeddertests",
     "core/fxcodec:embeddertests",
+    "core/fxcrt",
     "core/fxge:embeddertests",
     "fpdfsdk:embeddertests",
     "fpdfsdk/pwl:embeddertests",
diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn
index 3ccf31b..e4b1154 100644
--- a/core/fxcrt/BUILD.gn
+++ b/core/fxcrt/BUILD.gn
@@ -88,7 +88,7 @@
     "../../fpdfsdk/*",
     "../../fxbarcode:*",
     "../../fxjs:*",
-    "../../testing:test_support",
+    "../../testing:*",
     "../../testing/fuzzers/*",
     "../../third_party:fx_agg",
     "../../third_party:fx_lcms2",
diff --git a/fpdfsdk/BUILD.gn b/fpdfsdk/BUILD.gn
index fcea031..0194ee5 100644
--- a/fpdfsdk/BUILD.gn
+++ b/fpdfsdk/BUILD.gn
@@ -70,8 +70,6 @@
     "ipdfsdk_pauseadapter.h",
   ]
 
-  defines = [ "FPDF_IMPLEMENTATION" ]
-
   configs += [ "../:pdfium_core_config" ]
   deps = [
     "../:pdfium_public_headers",
diff --git a/testing/BUILD.gn b/testing/BUILD.gn
index 1f31d74..d2a0411 100644
--- a/testing/BUILD.gn
+++ b/testing/BUILD.gn
@@ -31,8 +31,9 @@
     "resources/",
   ]
   deps = [
-    "../:pdfium",
+    "../:pdfium_public_headers",
     "../core/fdrm",
+    "../core/fxcrt",
     "image_diff",
   ]
   configs += [ "../:pdfium_core_config" ]
@@ -88,8 +89,10 @@
 
   deps = [
     ":test_support",
-    "../:pdfium",
+    "../:pdfium_public_headers",
     "../core/fdrm",
+    "../core/fxcrt",
+    "../third_party:pdfium_base",
     "//testing/gmock",
     "//testing/gtest",
   ]
diff --git a/testing/fuzzers/BUILD.gn b/testing/fuzzers/BUILD.gn
index 038d2ce..60623c3 100644
--- a/testing/fuzzers/BUILD.gn
+++ b/testing/fuzzers/BUILD.gn
@@ -15,9 +15,7 @@
   include_dirs = [ "../.." ]
 }
 
-# "pdfium_fuzzer"s (which may depend on PDFium internals) should be added to
-# |fuzzer_list|.  "pdfium_public_fuzzer"s (which can only depend on PDFium's
-# public API) should be added to |public_fuzzer_list|.
+# All possible "pdfium_fuzzer"s.
 fuzzer_list = [
   "pdf_cmap_fuzzer",
   "pdf_codec_a85_fuzzer",
@@ -31,8 +29,8 @@
   "pdf_psengine_fuzzer",
   "pdf_streamparser_fuzzer",
   "pdf_xml_fuzzer",
+  "pdfium_fuzzer",
 ]
-public_fuzzer_list = [ "pdfium_fuzzer" ]
 if (pdf_enable_v8) {
   fuzzer_list += [
     "pdf_cjs_util_fuzzer",
@@ -46,10 +44,8 @@
       "pdf_codec_jpeg_fuzzer",
       "pdf_css_fuzzer",
       "pdf_fm2js_fuzzer",
-      "pdf_formcalc_fuzzer",
-    ]
-    public_fuzzer_list += [
       "pdf_formcalc_context_fuzzer",
+      "pdf_formcalc_fuzzer",
       "pdfium_xfa_fuzzer",
     ]
     if (pdf_enable_xfa_bmp) {
@@ -73,11 +69,41 @@
 group("fuzzers") {
   testonly = true
   deps = []
-  foreach(fuzzer, fuzzer_list + public_fuzzer_list) {
+  foreach(fuzzer, fuzzer_list) {
     deps += [ ":${fuzzer}_src" ]
   }
 }
 
+jumbo_source_set("fuzzer_init") {
+  testonly = true
+  sources = [
+    "pdf_fuzzer_init.cc",
+  ]
+  include_dirs = [ "../.." ]
+  deps = [
+    "../../:pdfium_public_headers",
+  ]
+}
+
+jumbo_source_set("fuzzer_init_public") {
+  testonly = true
+  sources = [
+    "pdf_fuzzer_init_public.cc",
+  ]
+  include_dirs = [ "../.." ]
+  deps = [
+    "../../:pdfium_public_headers",
+  ]
+  if (pdf_enable_v8) {
+    configs += [ "//v8:external_startup_data" ]
+    deps += [
+      "../:test_support",
+      "//v8",
+      "//v8:v8_libplatform",
+    ]
+  }
+}
+
 if (is_component_build) {
   group("fuzzer_impls") {
     deps = []
@@ -87,53 +113,40 @@
   }
 }
 
-jumbo_source_set("utils") {
+jumbo_source_set("fuzzer_helper") {
+  testonly = !is_component_build
+  sources = [
+    "pdfium_fuzzer_helper.cc",
+    "pdfium_fuzzer_helper.h",
+  ]
+  configs += [ ":fuzzer_config" ]
+  deps = [
+    "../../:pdfium_public_headers",
+    "../../third_party:pdfium_base",
+  ]
+}
+
+jumbo_source_set("fuzzer_utils") {
+  # In component builds, the pdfium target (which is not testonly) depends on
+  # the fuzzer sources, which may depend on this target, so add testonly only in
+  # non-component builds.
+  testonly = !is_component_build
   sources = [
     "pdfium_fuzzer_util.cc",
     "pdfium_fuzzer_util.h",
   ]
-
+  deps = [
+    "../../:pdfium_public_headers",
+  ]
   include_dirs = [ "../.." ]
-
-  # In component builds, the pdfium target (which is not testonly) depends on
-  # the fuzzer sources, which may depend on this target, so add testonly only in
-  # non-component builds.
-  if (!is_component_build) {
-    testonly = true
-  }
-}
-
-template("pdfium_public_fuzzer") {
-  jumbo_source_set(target_name + "_src") {
-    sources = invoker.sources + [
-                "pdfium_fuzzer_helper.cc",
-                "pdfium_fuzzer_helper.h",
-                "pdf_fuzzer_init_public.cc",
-              ]
-    deps = [
-      "../:test_support",
-      "../../:pdfium",
-    ]
-    if (defined(invoker.deps)) {
-      deps += invoker.deps
-    }
-    testonly = true
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      "//build/config/compiler:no_chromium_code",
-      ":fuzzer_config",
-    ]
-    if (pdf_enable_v8) {
-      configs += [ "//v8:external_startup_data" ]
-      deps += [
-        "//v8",
-        "//v8:v8_libplatform",
-      ]
-    }
-  }
 }
 
 template("pdfium_fuzzer") {
+  if (defined(invoker.public_fuzzer) && invoker.public_fuzzer) {
+    init_dep = ":fuzzer_init_public"
+  } else {
+    init_dep = ":fuzzer_init"
+  }
   if (is_component_build) {
     # In component builds, fuzzers are split into "_impl" and "_src" targets.
     # The "_impl" target exports the fuzzer implementation and gets statically
@@ -141,16 +154,17 @@
     # wrapper that imports the fuzzer from PDFium; this gets linked into the
     # real fuzzer executable.  In static builds, there's only a single "_src"
     # target that contains the implementation and statically links in PDFium.
+
     impl_name = target_name + "_impl"
     template_target_name = target_name
     jumbo_source_set("${target_name}_src") {
       testonly = true
       sources = [
         "component_fuzzer_template.cc",
-        "pdf_fuzzer_init.cc",
       ]
       deps = [
         "../../:pdfium",
+        init_dep,
       ]
       configs += [ ":fuzzer_config" ]
       defines = [ "FUZZER_IMPL=${template_target_name}" ]
@@ -169,9 +183,6 @@
       "//build/config/compiler:no_chromium_code",
       ":fuzzer_config",
     ]
-    if (pdf_enable_v8) {
-      configs += [ "//v8:external_startup_data" ]
-    }
     if (is_component_build) {
       # |export| should be consistent with FPDF_EXPORT In public/fpdfview.h.
       if (is_win) {
@@ -179,14 +190,14 @@
       } else {
         export = "__attribute__((visibility(\"default\")))"
       }
-      defines = [
-        "FPDF_IMPLEMENTATION",
-        "LLVMFuzzerTestOneInput=${export} ${template_target_name}",
-      ]
+      defines = [ "LLVMFuzzerTestOneInput=${export} ${template_target_name}" ]
+      deps += [ "../../:pdfium_public_headers" ]
     } else {
-      sources += [ "pdf_fuzzer_init.cc" ]
       testonly = true
-      deps += [ "../../:pdfium" ]
+      deps += [
+        "../../:pdfium",
+        init_dep,
+      ]
     }
   }
 }
@@ -351,11 +362,12 @@
       ]
     }
 
-    pdfium_public_fuzzer("pdf_formcalc_context_fuzzer") {
+    pdfium_fuzzer("pdf_formcalc_context_fuzzer") {
       sources = [
         "pdf_formcalc_context_fuzzer.cc",
       ]
       deps = [
+        ":fuzzer_helper",
         "../../:pdfium_public_headers",
         "../../core/fxcrt",
         "../../fpdfsdk",
@@ -363,6 +375,7 @@
         "../../fxjs",
         "../../xfa/fxfa",
       ]
+      public_fuzzer = true
     }
 
     pdfium_fuzzer("pdf_formcalc_fuzzer") {
@@ -375,13 +388,15 @@
       ]
     }
 
-    pdfium_public_fuzzer("pdfium_xfa_fuzzer") {
+    pdfium_fuzzer("pdfium_xfa_fuzzer") {
       sources = [
         "pdfium_xfa_fuzzer.cc",
       ]
       deps = [
+        ":fuzzer_helper",
         "../../:pdfium_public_headers",
       ]
+      public_fuzzer = true
     }
   }
 }
@@ -412,7 +427,7 @@
     "pdf_codec_fax_fuzzer.cc",
   ]
   deps = [
-    ":utils",
+    ":fuzzer_utils",
     "../../core/fxcodec",
   ]
 }
@@ -433,7 +448,7 @@
     "pdf_codec_jbig2_fuzzer.cc",
   ]
   deps = [
-    ":utils",
+    ":fuzzer_utils",
     "../../core/fpdfapi/parser",
     "../../core/fxcodec",
     "../../core/fxcrt",
@@ -514,8 +529,12 @@
   ]
 }
 
-pdfium_public_fuzzer("pdfium_fuzzer") {
+pdfium_fuzzer("pdfium_fuzzer") {
   sources = [
     "pdfium_fuzzer.cc",
   ]
+  deps = [
+    ":fuzzer_helper",
+  ]
+  public_fuzzer = true
 }
diff --git a/testing/fuzzers/component_fuzzer_template.cc b/testing/fuzzers/component_fuzzer_template.cc
index 7106417..89883f5 100644
--- a/testing/fuzzers/component_fuzzer_template.cc
+++ b/testing/fuzzers/component_fuzzer_template.cc
@@ -5,17 +5,14 @@
 #include <cstddef>
 #include <cstdint>
 
+#include "public/fpdfview.h"
+
 // This template is used in component builds to forward to the real fuzzers
-// which are exported from the PDFium shared library.  FUZZER_IMPL is a macro
-// defined at build time that contains the name of the real fuzzer.
+// which are exported from the PDFium shared library.
 
-#if defined(WIN32)
-#define IMPORT __declspec(dllimport)
-#else
-#define IMPORT
-#endif
-
-extern "C" IMPORT int FUZZER_IMPL(const uint8_t* data, size_t size);
+// FUZZER_IMPL is a macro defined at build time that contains the name of the
+// real fuzzer.
+extern "C" FPDF_EXPORT int FUZZER_IMPL(const uint8_t* data, size_t size);
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   return FUZZER_IMPL(data, size);