diff --git a/CMakeLists.txt b/CMakeLists.txt
index 30e1117fae..a840ac1730 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1107,7 +1107,7 @@ if(BUILD_TESTING)
       add_custom_target(fips_specific_tests_if_any)
     endif()
 
-    # Add macho parser tests if FIPS and on MacOS
+    # Add relevant tests if FIPS and on MacOS
     if(FIPS AND APPLE)
       add_custom_target(
         macho_parser_tests
@@ -1116,6 +1116,14 @@ if(BUILD_TESTING)
         DEPENDS test_macho_parser
       )
       add_dependencies(fips_specific_tests_if_any macho_parser_tests)
+
+      add_custom_target(
+        inject_hash_tests
+        COMMAND test_inject_hash
+        WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/util/fipstools/inject_hash/tests/
+        DEPENDS test_inject_hash
+      )
+      add_dependencies(fips_specific_tests_if_any inject_hash_tests)
     endif()
 
     # Read util/go_tests.txt into a CMake variable.
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
index b4a9154b4d..967bbb426e 100644
--- a/crypto/CMakeLists.txt
+++ b/crypto/CMakeLists.txt
@@ -315,6 +315,12 @@ else()
   file(COPY ${GENERATE_CODE_ROOT}/err_data.c DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/)
 endif()
 
+# We need to create a object library off of the generated err_data.c in so we can mark it as a dependency for our C inject hash implementation
+if(FIPS)
+  add_library(generated_err_data OBJECT err_data.c)
+  target_include_directories(generated_err_data PRIVATE ${PROJECT_SOURCE_DIR}/include)
+endif()
+
 set(DILITHIUM_SOURCES)
 if(ENABLE_DILITHIUM)
   set(
@@ -585,7 +591,6 @@ add_library(
   decrepit/x509/x509_decrepit.c
 
   ${CRYPTO_ARCH_SOURCES}
-  ${CRYPTO_ARCH_OBJECTS}
 )
 
 target_compile_definitions(crypto_objects PRIVATE BORINGSSL_IMPLEMENTATION)
@@ -654,24 +659,35 @@ if(FIPS_SHARED)
 
     build_libcrypto(crypto $<TARGET_OBJECTS:generated_fipsmodule>)
   else()
-    # On Apple and Linux platforms inject_hash.go can parse libcrypto and inject
+    # On Apple and Linux platforms inject_hash.go (inject_hash for Apple) can parse libcrypto and inject
     # the hash directly into the final library.
     build_libcrypto(crypto $<TARGET_OBJECTS:fipsmodule>)
     if (APPLE)
-      set(INJECT_HASH_APPLE_FLAG "-apple")
-    endif()
+      # Add subdirectory that handles building a stripped-down version of AWS-LC for use in calculating and injecting the FIPS integrity hash
+      add_subdirectory(fips_hashing)
+      add_subdirectory(${PROJECT_SOURCE_DIR}/util/fipstools/inject_hash inject_hash)
 
-    add_custom_command(
-      TARGET crypto POST_BUILD
-      COMMAND ${GO_EXECUTABLE} run
-      ${PROJECT_SOURCE_DIR}/util/fipstools/inject_hash/inject_hash.go
-      -o $<TARGET_FILE:crypto> -in-object $<TARGET_FILE:crypto> ${INJECT_HASH_APPLE_FLAG}
-      # The DEPENDS argument to a POST_BUILD rule appears to be ignored. Thus
-      # go_executable isn't used (as it doesn't get built), but we list this
-      # dependency anyway in case it starts working in some CMake version.
-      DEPENDS ../util/fipstools/inject_hash/inject_hash.go
-      WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+      set(INJECT_HASH_APPLE_FLAG "-f")
+      add_custom_command(
+        TARGET crypto POST_BUILD
+        COMMAND inject_hash
+        -p $<TARGET_FILE:crypto> -o $<TARGET_FILE:crypto> ${INJECT_HASH_APPLE_FLAG}
+        DEPENDS inject_hash
+        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
     )
+    else()
+      add_custom_command(
+        TARGET crypto POST_BUILD
+        COMMAND ${GO_EXECUTABLE} run
+        ${PROJECT_SOURCE_DIR}/util/fipstools/inject_hash/go/inject_hash.go
+        -o $<TARGET_FILE:crypto> -in-object $<TARGET_FILE:crypto> ${INJECT_HASH_APPLE_FLAG}
+        # The DEPENDS argument to a POST_BUILD rule appears to be ignored. Thus
+        # go_executable isn't used (as it doesn't get built), but we list this
+        # dependency anyway in case it starts working in some CMake version.
+        DEPENDS ../util/fipstools/inject_hash/go/inject_hash.go
+        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+      )
+    endif()
 
     # On macOS 11 and higher on Apple Silicon, codesigning is mandatory for
     # binaries to run. This applies to both executables and dylibs. An ad-hoc
diff --git a/crypto/fips_hashing/CMakeLists.txt b/crypto/fips_hashing/CMakeLists.txt
new file mode 100644
index 0000000000..b826dc69ae
--- /dev/null
+++ b/crypto/fips_hashing/CMakeLists.txt
@@ -0,0 +1,23 @@
+if(FIPS AND APPLE)
+  add_definitions(-DOPENSSL_NO_ASM=1)
+  remove_definitions(-DBORINGSSL_FIPS -DFIPS_ENTROPY_SOURCE_JITTER_CPU -DFIPS_ENTROPY_SOURCE_PASSIVE)
+
+  add_library(
+    fips_hashing
+    
+    STATIC
+
+    fips_hashing.c
+
+    ../mem.c
+    ../thread_none.c
+    ../thread_pthread.c
+
+    ../err/err.c
+    $<TARGET_OBJECTS:generated_err_data>
+    ../decrepit/ripemd/ripemd.c
+  )
+
+  SET_TARGET_PROPERTIES(fips_hashing PROPERTIES LINKER_LANGUAGE C)
+  target_include_directories(fips_hashing PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
+endif()
diff --git a/crypto/fips_hashing/fips_hashing.c b/crypto/fips_hashing/fips_hashing.c
new file mode 100644
index 0000000000..620739fb3b
--- /dev/null
+++ b/crypto/fips_hashing/fips_hashing.c
@@ -0,0 +1,16 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0 OR ISC
+
+#include "../fipsmodule/delocate.h"
+
+#include "../fipsmodule/evp/p_hmac.c"
+#include "../fipsmodule/digest/digest.c"
+#include "../fipsmodule/digest/digests.c"
+#include "../fipsmodule/hmac/hmac.c"
+#include "../fipsmodule/md4/md4.c"
+#include "../fipsmodule/md5/md5.c"
+#include "../fipsmodule/sha/keccak1600.c"
+#include "../fipsmodule/sha/sha1.c"
+#include "../fipsmodule/sha/sha256.c"
+#include "../fipsmodule/sha/sha3.c"
+#include "../fipsmodule/sha/sha512.c"
diff --git a/crypto/fipsmodule/CMakeLists.txt b/crypto/fipsmodule/CMakeLists.txt
index 220ef5d47b..2b99ebc76b 100644
--- a/crypto/fipsmodule/CMakeLists.txt
+++ b/crypto/fipsmodule/CMakeLists.txt
@@ -404,7 +404,7 @@ if(FIPS_DELOCATE)
   set_target_properties(bcm_hashunset PROPERTIES LINKER_LANGUAGE C)
 
   go_executable(inject_hash
-                boringssl.googlesource.com/boringssl/util/fipstools/inject_hash)
+                boringssl.googlesource.com/boringssl/util/fipstools/inject_hash/go)
   add_custom_command(
     OUTPUT bcm.o
     COMMAND ./inject_hash -o bcm.o -in-archive $<TARGET_FILE:bcm_hashunset>
diff --git a/util/fipstools/CMakeLists.txt b/util/fipstools/CMakeLists.txt
index 6a0d83efba..3947dca23b 100644
--- a/util/fipstools/CMakeLists.txt
+++ b/util/fipstools/CMakeLists.txt
@@ -7,5 +7,6 @@ if(FIPS AND BUILD_TESTING)
   target_link_libraries(test_fips crypto)
   target_include_directories(test_fips BEFORE PRIVATE ${PROJECT_BINARY_DIR}/symbol_prefix_include)
 
+  add_subdirectory(inject_hash/tests)
   add_subdirectory(inject_hash/macho_parser/tests)
 endif()
diff --git a/util/fipstools/inject_hash/CMakeLists.txt b/util/fipstools/inject_hash/CMakeLists.txt
new file mode 100644
index 0000000000..bb115168a3
--- /dev/null
+++ b/util/fipstools/inject_hash/CMakeLists.txt
@@ -0,0 +1,11 @@
+if(FIPS AND APPLE)
+    add_executable(
+        inject_hash
+
+        inject_hash.c
+        inject_hash_lib.c
+        macho_parser/macho_parser.c
+    )
+    target_link_libraries(inject_hash PUBLIC fips_hashing)
+    target_include_directories(inject_hash PRIVATE ${PROJECT_SOURCE_DIR}/include)
+endif()
diff --git a/util/fipstools/inject_hash/macho_parser/common.h b/util/fipstools/inject_hash/common.h
similarity index 60%
rename from util/fipstools/inject_hash/macho_parser/common.h
rename to util/fipstools/inject_hash/common.h
index d1d81fbaaa..4954c32f5f 100644
--- a/util/fipstools/inject_hash/macho_parser/common.h
+++ b/util/fipstools/inject_hash/common.h
@@ -3,6 +3,10 @@
 
 #ifndef COMMON_H
 #define COMMON_H
+#ifdef __cplusplus
+extern "C"
+{
+#endif
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -14,4 +18,11 @@
     fprintf(stderr, "\n"); \
 } while(0)
 
+int inject_hash_no_write(const char *o_input, int apple_flag,
+                         uint8_t **object_bytes, size_t *object_bytes_size);
+int inject_hash(int argc, char *argv[]);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
 #endif
diff --git a/util/fipstools/inject_hash/inject_hash.go b/util/fipstools/inject_hash/go/inject_hash.go
similarity index 100%
rename from util/fipstools/inject_hash/inject_hash.go
rename to util/fipstools/inject_hash/go/inject_hash.go
diff --git a/util/fipstools/inject_hash/inject_hash.c b/util/fipstools/inject_hash/inject_hash.c
new file mode 100644
index 0000000000..ae165b38f3
--- /dev/null
+++ b/util/fipstools/inject_hash/inject_hash.c
@@ -0,0 +1,11 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0 OR ISC
+
+#include "common.h"
+
+int main(int argc, char *argv[]) {
+    if (!inject_hash(argc, argv)) {
+        exit(EXIT_FAILURE);
+    }
+    exit(EXIT_SUCCESS);
+}
diff --git a/util/fipstools/inject_hash/inject_hash_lib.c b/util/fipstools/inject_hash/inject_hash_lib.c
new file mode 100644
index 0000000000..c6ed41fad2
--- /dev/null
+++ b/util/fipstools/inject_hash/inject_hash_lib.c
@@ -0,0 +1,355 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0 OR ISC
+
+#include <unistd.h>
+
+#include "common.h"
+#include "macho_parser/macho_parser.h"
+
+#include <openssl/base.h>
+#include <openssl/hmac.h>
+#include <openssl/mem.h>
+
+static uint8_t* read_object(const char *filename, size_t *size) {
+    FILE *file = fopen(filename, "rb");
+    uint8_t *object_bytes = NULL;
+    if (file == NULL) {
+        LOG_ERROR("Error opening file %s", filename);
+        goto end;
+    }
+
+    fseek(file, 0, SEEK_END);
+    size_t file_size = ftell(file);
+    fseek(file, 0, SEEK_SET);
+
+    object_bytes = malloc(file_size);
+    if (object_bytes == NULL) {
+        LOG_ERROR("Error allocating object_bytes memory");
+        goto end;
+    }
+
+    *size = fread(object_bytes, 1, file_size, file);
+    if (*size != file_size) {
+        LOG_ERROR("Error reading from file %s", filename);
+        free(object_bytes);
+        object_bytes = NULL;
+        goto end;
+    }
+
+end:
+    fclose(file);
+    return object_bytes;
+}
+
+static int write_object(const char *filename, uint8_t *bytes, size_t size) {
+    int ret = 0;
+
+    FILE *file = fopen(filename, "wb");
+    if (file == NULL) {
+        LOG_ERROR("Error opening file %s", filename);
+        goto end;
+    }
+
+    size_t written = fwrite(bytes, sizeof(uint8_t), size, file);
+    if (written != size) {
+        LOG_ERROR("Error writing file %s", filename);
+        goto end;
+    }
+
+    ret = 1;
+
+end:
+    fclose(file);
+    return ret;
+}
+
+static uint32_t find_hash(uint8_t *object_bytes, size_t object_bytes_size, uint8_t *hash, size_t hash_size) {
+    uint8_t *ptr = memmem(object_bytes, object_bytes_size, hash, hash_size);
+    if (ptr == NULL) {
+        LOG_ERROR("Error finding hash in object");
+        return 0;
+    }
+
+    return ptr - object_bytes;
+}
+
+static void size_to_little_endian_bytes(size_t size, uint8_t *result) {
+    for (int i = 0; i < 8; i++) {
+        result[i] = (size >> (i * 8)) & 0xFF;
+    }
+}
+
+static int do_apple(const char *object_file, uint8_t **text_module, size_t *text_module_size, uint8_t **rodata_module, size_t *rodata_module_size) {
+    uint8_t *text_section = NULL;
+    size_t text_section_size;
+    uint32_t text_section_offset;
+
+    uint8_t *rodata_section = NULL;
+    size_t rodata_section_size;
+    uint32_t rodata_section_offset;
+
+    uint8_t *symbol_table = NULL;
+    size_t symbol_table_size;
+
+    uint8_t *string_table = NULL;
+    size_t string_table_size;
+
+    uint32_t text_start;
+    uint32_t text_end;
+    uint32_t rodata_start;
+    uint32_t rodata_end;
+
+    machofile *macho = NULL;
+
+    int ret = 0;
+    
+    macho = malloc(sizeof(machofile));
+    if (macho == NULL) {
+        LOG_ERROR("Error allocating memory for machofile");
+        goto end;
+    }
+    if (read_macho_file(object_file, macho)) {
+        text_section = get_macho_section_data(object_file, macho, "__text", &text_section_size, &text_section_offset);
+        if (text_section == NULL) {
+            LOG_ERROR("Error getting text_section");
+            goto end;
+        }
+        // Not guaranteed to have a rodata section so we won't error out if this returns NULL
+        rodata_section = get_macho_section_data(object_file, macho, "__const", &rodata_section_size, &rodata_section_offset);
+        symbol_table = get_macho_section_data(object_file, macho, "__symbol_table", &symbol_table_size, NULL);
+        if (symbol_table == NULL) {
+            LOG_ERROR("Error getting symbol table");
+            goto end;
+        }
+        string_table = get_macho_section_data(object_file, macho, "__string_table", &string_table_size, NULL);
+        if (string_table == NULL) {
+            LOG_ERROR("Error getting string table");
+            goto end;
+        }
+
+        if(!find_macho_symbol_index(symbol_table, symbol_table_size, string_table, string_table_size, "_BORINGSSL_bcm_text_start", &text_section_offset, &text_start)) {
+            LOG_ERROR("Could not find .text module start symbol in object");
+            goto end;
+        }
+        if (!find_macho_symbol_index(symbol_table, symbol_table_size, string_table, string_table_size, "_BORINGSSL_bcm_text_end", &text_section_offset, &text_end)) {
+            LOG_ERROR("Could not find .text module end symbol in object");
+            goto end;
+        }
+        find_macho_symbol_index(symbol_table, symbol_table_size, string_table, string_table_size, "_BORINGSSL_bcm_rodata_start", &rodata_section_offset, &rodata_start);
+        find_macho_symbol_index(symbol_table, symbol_table_size, string_table, string_table_size, "_BORINGSSL_bcm_rodata_end", &rodata_section_offset, &rodata_end);
+
+
+        if ((!rodata_start) != (!rodata_section)) {
+            LOG_ERROR(".rodata start marker inconsistent with rodata section presence");
+            goto end;
+        }
+
+        if ((!rodata_start) != (!rodata_end)) {
+            LOG_ERROR(".rodata marker presence inconsistent");
+            goto end;
+        }
+
+        if (text_start > text_section_size || text_start > text_end || text_end > text_section_size) {
+            LOG_ERROR("Invalid .text module boundaires: start %x, end: %x, max: %zx", rodata_start, rodata_end, rodata_section_size);
+            goto end;
+        }
+
+        // Get text and rodata modules from text_section/rodata_section using obtained indices
+        *text_module_size = text_end - text_start;
+        *text_module = malloc(*text_module_size);
+        if (*text_module == NULL) {
+            LOG_ERROR("Error allocating memory for text_module");
+            goto end;
+        }
+        memcpy(*text_module, text_section + text_start, *text_module_size);
+
+        if (rodata_section != NULL) {
+            *rodata_module_size = rodata_end - rodata_start;
+            *rodata_module = malloc(*rodata_module_size);
+            if (*rodata_module == NULL) {
+                LOG_ERROR("Error allocating memory for rodata module");
+                goto end;
+            }
+            memcpy(*rodata_module, rodata_section + rodata_start, *rodata_module_size);
+        }
+        ret = 1;
+    } else {
+        LOG_ERROR("Error reading Mach-O file");
+        goto end;
+    }
+
+end:
+    free(text_section);
+    free(rodata_section);
+    free(symbol_table);
+    free(string_table);
+    free(macho->sections);
+    free(macho);
+
+    return ret;
+}
+
+int inject_hash_no_write(const char *o_input, int apple_flag,
+                         uint8_t **object_bytes, size_t *object_bytes_size) {
+    int ret = 0;
+
+    uint8_t uninit_hash[] = {
+        0xae, 0x2c, 0xea, 0x2a, 0xbd, 0xa6, 0xf3, 0xec, 
+        0x97, 0x7f, 0x9b, 0xf6, 0x94, 0x9a, 0xfc, 0x83, 
+        0x68, 0x27, 0xcb, 0xa0, 0xa0, 0x9f, 0x6b, 0x6f, 
+        0xde, 0x52, 0xcd, 0xe2, 0xcd, 0xff, 0x31, 0x80,
+    };
+
+    uint8_t *text_module = NULL;
+    size_t text_module_size;
+    uint8_t *rodata_module = NULL;
+    size_t rodata_module_size;
+
+    uint8_t *calculated_hash = NULL;
+    uint8_t length_bytes[8];
+
+    uint32_t hash_index;
+
+
+    *object_bytes = read_object(o_input, object_bytes_size);
+    if (object_bytes == NULL) {
+        LOG_ERROR("Error reading object bytes from %s", o_input);
+        goto end;
+    }
+
+    if (apple_flag == 1) {
+        if (!do_apple(o_input, &text_module, &text_module_size, &rodata_module, &rodata_module_size)) {
+            LOG_ERROR("Error getting text and rodata modules from Apple OS object");
+            goto end;
+        }
+    }
+    // TODO: else handle linux, not needed for Apple platforms
+
+    if (text_module == NULL || rodata_module == NULL) {
+        LOG_ERROR("Error getting text or rodata section");
+        goto end;
+    }
+
+    hash_index = find_hash(*object_bytes, *object_bytes_size, uninit_hash, sizeof(uninit_hash));
+    if (!hash_index) {
+        LOG_ERROR("Error finding hash");
+        goto end;
+    }
+
+    uint8_t zero_key[64] = {0};
+    HMAC_CTX ctx;
+    if (!HMAC_Init(&ctx, &zero_key, sizeof(zero_key), EVP_sha256())) {
+        LOG_ERROR("Error in HMAC_Init()");
+        goto end;
+    }
+
+    if(rodata_module != NULL) {
+        size_to_little_endian_bytes(text_module_size, length_bytes);
+        if (!HMAC_Update(&ctx, length_bytes, 8)) {
+            LOG_ERROR("Error in HMAC_Update() of textModuleSize");
+            goto end;
+        }
+        if (!HMAC_Update(&ctx, text_module, text_module_size)) {
+            LOG_ERROR("Error in HMAC_Update() of textModule");
+            goto end;
+        }
+        size_to_little_endian_bytes(rodata_module_size, length_bytes);
+        if (!HMAC_Update(&ctx, length_bytes, 8)) {
+            LOG_ERROR("Error in HMAC_Update() of rodataModuleSize");
+            goto end;
+        }
+        if (!HMAC_Update(&ctx, rodata_module, rodata_module_size)) {
+            LOG_ERROR("Error in HMAC_Update() of rodataModule");
+            goto end;
+        }
+    } else {
+        if (!HMAC_Update(&ctx, text_module, text_module_size)) {
+            LOG_ERROR("Error in HMAC_Update() of textModule");
+            goto end;
+        }
+    }
+
+    calculated_hash = malloc(HMAC_size(&ctx));
+    if (calculated_hash == NULL) {
+        LOG_ERROR("Error allocating memory for calculated hash");
+        goto end;
+    }
+    unsigned int calculated_hash_len;
+    if (!HMAC_Final(&ctx, calculated_hash, &calculated_hash_len)) {
+        LOG_ERROR("Error in HMAC_Final()");
+        goto end;
+    }
+
+    // Print calculated hash for reference
+    printf("Calculated hash: ");
+    for (unsigned int i = 0; i < calculated_hash_len; i++) {
+        printf("%02X", calculated_hash[i]);
+    }
+    printf("\n");
+
+    memcpy(*object_bytes + hash_index, calculated_hash, calculated_hash_len);
+
+    ret = 1;
+
+end:
+    free(text_module);
+    free(rodata_module);
+    free(calculated_hash);
+    return ret;
+}
+
+int inject_hash(int argc, char *argv[]) {
+    char *ar_input = NULL;
+    char *o_input = NULL;
+    char *out_path = NULL;
+    int apple_flag = 0;
+
+    int ret = 0;
+
+    uint8_t *object_bytes = NULL;
+    size_t object_bytes_size;
+
+    int opt;
+    while ((opt = getopt(argc, argv, "a:o:p:f")) != -1) {
+        switch(opt) {
+            case 'a':
+                ar_input = optarg;
+                break;
+            case 'o':
+                o_input = optarg;
+                break;
+            case 'p':
+                out_path = optarg;
+                break;
+            case 'f':
+                apple_flag = 1;
+                break;
+            case '?':
+            default:
+                LOG_ERROR("Usage: %s [-a in-archive] [-o in-object] [-p out-path] [-f apple-flag]", argv[0]);
+                goto end;
+        }
+    }
+
+    if ((ar_input == NULL && o_input == NULL) || out_path == NULL) {
+        LOG_ERROR("Usage: %s [-a in-archive] [-o in-object] [-p out-path] [-f apple-flag]", argv[0]);
+        LOG_ERROR("Note that either the -a or -o option and -p options are required.");
+        goto end;
+    }
+
+    if (!inject_hash_no_write(o_input, apple_flag, &object_bytes,
+                              &object_bytes_size)) {
+        LOG_ERROR("Error encountered while injecting hash");
+        goto end;
+    }
+
+    if (!write_object(out_path, object_bytes, object_bytes_size)) {
+        LOG_ERROR("Error writing file %s", out_path);
+        goto end;
+    }
+
+    ret = 1;
+end:
+    free(object_bytes);
+    return ret;
+}
diff --git a/util/fipstools/inject_hash/macho_parser/macho_parser.c b/util/fipstools/inject_hash/macho_parser/macho_parser.c
index b24c343403..94272eedc6 100644
--- a/util/fipstools/inject_hash/macho_parser/macho_parser.c
+++ b/util/fipstools/inject_hash/macho_parser/macho_parser.c
@@ -3,7 +3,7 @@
 
 #include <stdint.h>
 
-#include "common.h"
+#include "../common.h"
 #include "macho_parser.h"
 
 #define TEXT_INDEX 0
@@ -106,15 +106,13 @@ int read_macho_file(const char *filename, machofile *macho) {
     if (file != NULL) {
         fclose(file);
     }
+    if (!ret) {
+        free(macho->sections);
+        macho->sections = NULL;
+    }
     return ret;
 }
 
-void free_macho_file(machofile *macho) {
-    free(macho->sections);
-    free(macho);
-    macho = NULL;
-}
-
 uint8_t* get_macho_section_data(const char *filename, machofile *macho, const char *section_name, size_t *size, uint32_t *offset) {
     FILE *file = NULL;
     uint8_t *ret = NULL;
@@ -174,9 +172,9 @@ uint8_t* get_macho_section_data(const char *filename, machofile *macho, const ch
     return ret;
 }
 
-uint32_t find_macho_symbol_index(uint8_t *symbol_table_data, size_t symbol_table_size, uint8_t *string_table_data, size_t string_table_size, const char *symbol_name, uint32_t *base) {
+int find_macho_symbol_index(uint8_t *symbol_table_data, size_t symbol_table_size, uint8_t *string_table_data, size_t string_table_size, const char *symbol_name, uint32_t *base, uint32_t *index) {
     char* string_table = NULL;
-    uint32_t ret = 0;
+    int ret = 0;
 
     if (symbol_table_data == NULL || string_table_data == NULL) {
         LOG_ERROR("Symbol and string table pointers cannot be null to find the symbol index");
@@ -191,12 +189,25 @@ uint32_t find_macho_symbol_index(uint8_t *symbol_table_data, size_t symbol_table
     memcpy(string_table, string_table_data, string_table_size);
 
     int found = 0;
-    size_t index = 0;
     for (size_t i = 0; i < symbol_table_size / sizeof(struct nlist_64); i++) {
         struct nlist_64 *symbol = (struct nlist_64 *)(symbol_table_data + i * sizeof(struct nlist_64));
+
+        // Skip debugging symbols
+        //
+        // #define	N_STAB	0xe0  /* if any of these bits set, a symbolic debugging entry */
+        //
+        // "Only symbolic debugging entries have some of the N_STAB bits set and if any of these bits are set then it is
+        // a symbolic debugging entry (a stab).  In which case then the values of the n_type field (the entire field)
+        // are given in <mach-o/stab.h>"
+        //
+        // https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/nlist.h
+        if (symbol->n_type & N_STAB) {
+            continue;
+        }
+
         if (strcmp(symbol_name, &string_table[symbol->n_un.n_strx]) == 0) {
             if (found == 0) {
-                index = symbol->n_value;
+                *index = symbol->n_value;
                 found = 1;
             } else {
                 LOG_ERROR("Duplicate symbol %s found", symbol_name);
@@ -209,9 +220,9 @@ uint32_t find_macho_symbol_index(uint8_t *symbol_table_data, size_t symbol_table
         goto end;
     }
     if (base != NULL) {
-        index = index - *base;
+        *index = *index - *base;
     }
-    ret = index;
+    ret = 1;
 
 end:
     free(string_table);
diff --git a/util/fipstools/inject_hash/macho_parser/macho_parser.h b/util/fipstools/inject_hash/macho_parser/macho_parser.h
index a11463fc87..0d788ebac6 100644
--- a/util/fipstools/inject_hash/macho_parser/macho_parser.h
+++ b/util/fipstools/inject_hash/macho_parser/macho_parser.h
@@ -24,20 +24,19 @@ typedef struct {
 } machofile;
 
 // read_macho_file reads a Mach-O file [in] and populates a machofile struct [out] with its contents.
+// Assume that the machofile pointer provided is already allocated but is otherwise untouched.
 // It returns 0 on failure, 1 on success.
 int read_macho_file(const char *filename, machofile *macho);
 
-// free_macho_file frees the memory allocated to a machofile struct [in]
-void free_macho_file(machofile *macho);
-
 // get_macho_section_data retrieves data from a specific section [in] the provided Mach-O file [in].
 // In addition to returning a pointer to the retrieved data, or NULL if it doesn't find said section,
 // it also populates the size [out] & offset [out] pointers provided they are not NULL.
 uint8_t* get_macho_section_data(const char* filename, machofile *macho, const char *section_name, size_t *size, uint32_t *offset);
 
 // find_macho_symbol_index finds the index of a symbol [in] in the Mach-O file's [in] symbol table.
-// It returns the index on success, and 0 on failure.
-uint32_t find_macho_symbol_index(uint8_t *symbol_table_data, size_t symbol_table_size, uint8_t *string_table_data, size_t string_table_size, const char *symbol_name, uint32_t *base);
+// It stores the result in index [out].
+// It returns 1 on success, and 0 on failure.
+int find_macho_symbol_index(uint8_t *symbol_table_data, size_t symbol_table_size, uint8_t *string_table_data, size_t string_table_size, const char *symbol_name, uint32_t *base, uint32_t *index);
 
 #ifdef __cplusplus
 } // extern "C"
diff --git a/util/fipstools/inject_hash/macho_parser/tests/macho_tests.cc b/util/fipstools/inject_hash/macho_parser/tests/macho_tests.cc
index 38c921ba12..c46e527533 100644
--- a/util/fipstools/inject_hash/macho_parser/tests/macho_tests.cc
+++ b/util/fipstools/inject_hash/macho_parser/tests/macho_tests.cc
@@ -3,7 +3,7 @@
 
 #include <assert.h>
 
-#include "../common.h"
+#include "../../common.h"
 #include "macho_tests.h"
 
 #define TEST_FILE "test_macho"
@@ -60,7 +60,10 @@ TEST_F(MachoTestFixture, TestFindMachoSymbolIndex) {
     symbol_table.reset(get_macho_section_data(TEST_FILE, expected_macho, "__symbol_table", &symbol_table_size, NULL));
     string_table.reset(get_macho_section_data(TEST_FILE, expected_macho, "__string_table", &string_table_size, NULL));
 
-    uint32_t symbol1_index = find_macho_symbol_index(symbol_table.get(), symbol_table_size, string_table.get(), string_table_size, "symbol1", NULL);
+    uint32_t symbol1_index;
+    if (!find_macho_symbol_index(symbol_table.get(), symbol_table_size, string_table.get(), string_table_size, "symbol1", NULL, &symbol1_index)) {
+        LOG_ERROR("Could not find symbol index");
+    }
 
     ASSERT_EQ(symbol1_index, expected_symbol1_ind);
 }
diff --git a/util/fipstools/inject_hash/macho_parser/tests/macho_tests.h b/util/fipstools/inject_hash/macho_parser/tests/macho_tests.h
index 33783f60a5..d5c58f29f2 100644
--- a/util/fipstools/inject_hash/macho_parser/tests/macho_tests.h
+++ b/util/fipstools/inject_hash/macho_parser/tests/macho_tests.h
@@ -269,7 +269,8 @@ class MachoTestFixture : public ::testing::Test {
     }
 
     static void TearDownTestSuite() {
-        free_macho_file(expected_macho);
+        free(expected_macho->sections);
+        free(expected_macho);
         free(expected_symtab);
         if (remove(TEST_FILE) != 0) {
             LOG_ERROR("Error deleting %s", TEST_FILE);
diff --git a/util/fipstools/inject_hash/tests/CMakeLists.txt b/util/fipstools/inject_hash/tests/CMakeLists.txt
new file mode 100644
index 0000000000..39b071d14a
--- /dev/null
+++ b/util/fipstools/inject_hash/tests/CMakeLists.txt
@@ -0,0 +1,25 @@
+if(FIPS AND APPLE)
+    add_subdirectory(test_libs)
+
+    add_executable(
+        test_inject_hash
+
+        inject_hash_tests.cc
+        ../inject_hash_lib.c
+        ../macho_parser/macho_parser.c
+    )
+    add_dependencies(
+        test_inject_hash
+        
+        good_lib
+        bad_hash_lib
+        bad_marker_lib
+    )
+
+    target_link_libraries(
+        test_inject_hash
+
+        test_support_lib
+        boringssl_gtest_main
+    )
+endif()
diff --git a/util/fipstools/inject_hash/tests/inject_hash_tests.cc b/util/fipstools/inject_hash/tests/inject_hash_tests.cc
new file mode 100644
index 0000000000..6a53ddb28c
--- /dev/null
+++ b/util/fipstools/inject_hash/tests/inject_hash_tests.cc
@@ -0,0 +1,66 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0 OR ISC
+
+#include <assert.h>
+#include <gtest/gtest.h>
+#include <iostream>
+#include <string>
+
+#include "../common.h"
+
+// Paths below are based on expected location of library artifacts relative
+// to the location of this test executable.
+// TODO: change this based on platform, we only care about Apple for now
+#define GOOD_LIB_NAME "test_libs/libgood_lib.dylib"
+#define BAD_HASH_LIB_NAME "test_libs/libbad_hash_lib.dylib"
+#define BAD_MARKER_LIB_NAME "test_libs/libbad_marker_lib.dylib"
+
+class InjectHashTestFixture : public ::testing::Test {
+protected:
+    const std::string good_lib_filename = GOOD_LIB_NAME;
+    const std::string bad_hash_lib_filename = BAD_HASH_LIB_NAME;
+    const std::string bad_marker_lib_filename = BAD_MARKER_LIB_NAME;
+};
+
+TEST_F(InjectHashTestFixture, TestGoodLib) {
+    std::unique_ptr<uint8_t> object_bytes(nullptr);
+    size_t object_bytes_size;
+
+    uint8_t *object_bytes_ptr = object_bytes.get();
+
+    ASSERT_EQ(1, inject_hash_no_write(good_lib_filename.c_str(), 1, &object_bytes_ptr, &object_bytes_size));
+}
+
+TEST_F(InjectHashTestFixture, TestBadHashLib) {
+    std::unique_ptr<uint8_t> object_bytes(nullptr);
+    size_t object_bytes_size;
+
+    uint8_t *object_bytes_ptr = object_bytes.get();
+
+    int inject_hash_ret;
+    testing::internal::CaptureStderr();
+
+    inject_hash_ret = inject_hash_no_write(bad_hash_lib_filename.c_str(), 1,
+                             &object_bytes_ptr, &object_bytes_size);
+    std::string captured_stderr = testing::internal::GetCapturedStderr();
+
+    ASSERT_EQ(0, inject_hash_ret);
+    EXPECT_TRUE(captured_stderr.find("Error finding hash") != std::string::npos);
+}
+
+TEST_F(InjectHashTestFixture, TestBadMarkerLib) {
+    std::unique_ptr<uint8_t> object_bytes(nullptr);
+    size_t object_bytes_size;
+
+    uint8_t *object_bytes_ptr = object_bytes.get();
+
+    int inject_hash_ret;
+    testing::internal::CaptureStderr();
+
+    inject_hash_ret = inject_hash_no_write(bad_marker_lib_filename.c_str(), 1,
+                             &object_bytes_ptr, &object_bytes_size);
+    std::string captured_stderr = testing::internal::GetCapturedStderr();
+
+    ASSERT_EQ(0, inject_hash_ret);
+    EXPECT_TRUE(captured_stderr.find("Could not find .text module start symbol in object") != std::string::npos);
+}
diff --git a/util/fipstools/inject_hash/tests/test_libs/CMakeLists.txt b/util/fipstools/inject_hash/tests/test_libs/CMakeLists.txt
new file mode 100644
index 0000000000..f253dc75bf
--- /dev/null
+++ b/util/fipstools/inject_hash/tests/test_libs/CMakeLists.txt
@@ -0,0 +1,19 @@
+if(FIPS AND APPLE)
+    set(INJECT_HASH_TEST_LIB_COMPILE_OPTIONS
+        "-Wno-unused-function"
+        "-Wno-missing-prototypes"
+    )
+
+    function(create_test_lib name)
+        add_library(${name} testlib.c)
+        target_compile_options(${name} PRIVATE ${INJECT_HASH_TEST_LIB_COMPILE_OPTIONS})
+
+        if(NOT "${ARGN}" STREQUAL "")
+            target_compile_definitions(${name} PRIVATE ${ARGN})
+        endif()
+    endfunction()
+
+    create_test_lib(good_lib)
+    create_test_lib(bad_hash_lib "-DBAD_HASH")
+    create_test_lib(bad_marker_lib "-DBAD_MARKER")
+endif()
diff --git a/util/fipstools/inject_hash/tests/test_libs/testlib.c b/util/fipstools/inject_hash/tests/test_libs/testlib.c
new file mode 100644
index 0000000000..2ebbac06d9
--- /dev/null
+++ b/util/fipstools/inject_hash/tests/test_libs/testlib.c
@@ -0,0 +1,31 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0 OR ISC
+
+#include <stdio.h>
+#include <stdint.h>
+
+#ifndef BAD_MARKER
+const uint8_t *BORINGSSL_bcm_text_start(void) {
+  return NULL;
+}
+#endif
+const uint8_t *BORINGSSL_bcm_text_end(void){
+  return NULL;
+}
+
+
+const uint8_t BORINGSSL_bcm_text_hash[32] = {
+    0xae, 0x2c, 0xea, 0x2a, 0xbd, 0xa6, 0xf3, 0xec, 0x97, 0x7f, 0x9b,
+    0xf6, 0x94, 0x9a, 0xfc, 0x83, 0x68, 0x27, 0xcb, 0xa0, 0xa0, 0x9f,
+    0x6b, 0x6f, 0xde, 0x52, 0xcd, 0xe2, 0xcd, 0xff, 0x31, 
+#ifndef BAD_HASH
+    0x80,
+#else
+    0X81,
+#endif
+};
+
+const uint8_t BORINGSSL_bcm_rodata_start[16] =
+              {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; 
+const uint8_t BORINGSSL_bcm_rodata_end[16] =
+              {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};