chromium/ios/components/order_file/order_file.c

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/components/order_file/order_file_common.h"

#import <stdlib.h>

// Queue containing the ordered procedure calls.
OSQueueHead gCRWSanitizerQueue = OS_ATOMIC_QUEUE_INIT;

// Whether the guard variables have been initialized.
bool gCRWGuardsInitialized = false;

// Whether the addition of more procedure calls is allowed.
bool gCRWFinishedCollecting = false;

void __sanitizer_cov_trace_pc_guard_init(uint32_t* start,
                                         uint32_t* stop);  // NOLINT
void __sanitizer_cov_trace_pc_guard(uint32_t* guard);      // NOLINT

// Initialize the guards for each procedure call.
// Per clang documentation, this function will be called at least once per
// Dynamic Shared Object (DSO) and may be called more than once with the same
// values of start/stop.
void __sanitizer_cov_trace_pc_guard_init(uint32_t* start_guard,
                                         uint32_t* stop_guard) {  // NOLINT
  gCRWGuardsInitialized = true;
  if (start_guard == stop_guard || *start_guard) {
    return;
  }
  for (uint32_t* guard = start_guard; guard < stop_guard; guard++) {
    // Per https://clang.llvm.org/docs/SanitizerCoverage.html, *guard is
    // initialized to 1 and set to 0 below.
    *guard = 1;
  }
}

// Collect each procedure call in order once for each distinct call.
// Per clang documentation, this callback is inserted by the compiler on every
// edge in the control flow (some optimizations apply).
void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {  // NOLINT
  // If gGuardsInitialized is false, guard flags have not yet been initialized
  // and this is probably recording a +load method which  should still be
  // included in the order file.
  if (gCRWFinishedCollecting || (!(*guard) && gCRWGuardsInitialized)) {
    return;
  }
  // Make sure each procedure call is only collected once.
  *guard = 0;

  void* procedureCall = __builtin_return_address(0);
  CRWProcedureCallNode* node =
      (CRWProcedureCallNode*)malloc(sizeof(CRWProcedureCallNode));
  *node = (CRWProcedureCallNode){procedureCall, NULL};
  OSAtomicEnqueue(&gCRWSanitizerQueue, node,
                  offsetof(CRWProcedureCallNode, next));
}