Merge "Remove obsolete __clang__ check (and `-D`)." into main
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index ff3d498..e89acb5 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -567,9 +567,7 @@
       continue;
     }
 
-    if (phdr->p_align > maximum_alignment) {
-      maximum_alignment = phdr->p_align;
-    }
+    maximum_alignment = std::max(maximum_alignment, static_cast<size_t>(phdr->p_align));
   }
 
 #if defined(__LP64__)
@@ -579,6 +577,30 @@
 #endif
 }
 
+// Returns the minimum p_align associated with a loadable segment in the ELF
+// program header table. Used to determine if the program alignment is compatible
+// with the page size of this system.
+size_t phdr_table_get_minimum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count) {
+  size_t minimum_alignment = page_size();
+
+  for (size_t i = 0; i < phdr_count; ++i) {
+    const ElfW(Phdr)* phdr = &phdr_table[i];
+
+    // p_align must be 0, 1, or a positive, integral power of two.
+    if (phdr->p_type != PT_LOAD || ((phdr->p_align & (phdr->p_align - 1)) != 0)) {
+      continue;
+    }
+
+    if (phdr->p_align <= 1) {
+      continue;
+    }
+
+    minimum_alignment = std::min(minimum_alignment, static_cast<size_t>(phdr->p_align));
+  }
+
+  return minimum_alignment;
+}
+
 // Reserve a virtual address range such that if it's limits were extended to the next 2**align
 // boundary, it would not overlap with any existing mappings.
 static void* ReserveWithAlignmentPadding(size_t size, size_t mapping_align, size_t start_align,
@@ -830,6 +852,15 @@
 }
 
 bool ElfReader::LoadSegments() {
+  size_t min_palign = phdr_table_get_minimum_alignment(phdr_table_, phdr_num_);
+  // Only enforce this on 16 KB systems. Apps may rely on undefined behavior
+  // here on 4 KB systems, which is the norm before this change is introduced.
+  if (kPageSize >= 16384 && min_palign < kPageSize) {
+    DL_ERR("\"%s\" program alignment (%zu) cannot be smaller than system page size (%zu)",
+           name_.c_str(), min_palign, kPageSize);
+    return false;
+  }
+
   for (size_t i = 0; i < phdr_num_; ++i) {
     const ElfW(Phdr)* phdr = &phdr_table_[i];
 
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index aab9018..e865a03 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -126,6 +126,7 @@
                                 ElfW(Addr)* min_vaddr = nullptr, ElfW(Addr)* max_vaddr = nullptr);
 
 size_t phdr_table_get_maximum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count);
+size_t phdr_table_get_minimum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count);
 
 int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
                                 ElfW(Addr) load_bias, bool should_pad_segments,
diff --git a/tests/headers/posix/unistd_h.c b/tests/headers/posix/unistd_h.c
index 0b2cee5..f66609d 100644
--- a/tests/headers/posix/unistd_h.c
+++ b/tests/headers/posix/unistd_h.c
@@ -331,6 +331,9 @@
   FUNCTION(fdatasync, int (*f)(int));
   FUNCTION(fexecve, int (*f)(int, char* const[], char* const[]));
   FUNCTION(fork, pid_t (*f)(void));
+#if !defined(__GLIBC__) // Our glibc is too old.
+  FUNCTION(_Fork, pid_t (*f)(void));
+#endif
   FUNCTION(fpathconf, long (*f)(int, int));
   FUNCTION(fsync, int (*f)(int));
   FUNCTION(ftruncate, int (*f)(int, off_t));