Auto-generate header of struct offsets/sizes

Code written in assembly needs to be told the offsets of struct members.
Currently these are hardcoded and static_assert-ed at compile time,
requiring manual updates every time struct declarations change.
This patch adds a mechanism for auto-generating a header with these
constants.

It uses a "hack" similar to other projects, e.g. Linux, where the
integer constant is used as an immediate in an inline assembly block.
The file is compiled and the constant extracted by a script which
generates the header file. For easy grep-ing, the constant is compiled
into a '.ascii' string, surrounded by magic strings, and extracted using
the 'strings' binutils tool.

To guarantee correctness, the same source file is compiled again as part
of the Hafnium binary but this time the declarations are converted to
static_asserts which check the values.

Fix: 120137356
Test: ./kokoru/ubuntu/build.sh
Change-Id: I551126519675e73cb01b4beb4ff9b1200b9b3de7
diff --git a/build/BUILD.gn b/build/BUILD.gn
index 1adba35..8ce649c 100644
--- a/build/BUILD.gn
+++ b/build/BUILD.gn
@@ -39,6 +39,9 @@
     "//inc",
     "//inc/vmapi",
     "//src/arch/${plat_arch}/inc",
+
+    # Auto-generated headers using the 'offset_size_header' build rule.
+    "${root_gen_dir}/offset_size_header",
   ]
 
   defines = [
diff --git a/build/toolchain/embedded.gni b/build/toolchain/embedded.gni
index 476873c..31f7894 100644
--- a/build/toolchain/embedded.gni
+++ b/build/toolchain/embedded.gni
@@ -78,6 +78,15 @@
       ]
     }
 
+    tool("alink") {
+      outfile = "{{target_out_dir}}/{{target_output_name}}.a"
+      command = "rm -f $outfile && ${invoker.ar} -rc $outfile {{inputs}}"
+      description = "ALINK $outfile"
+      outputs = [
+        outfile,
+      ]
+    }
+
     tool("stamp") {
       command = "touch {{output}}"
       description = "STAMP {{output}}"
@@ -108,6 +117,7 @@
   embedded_cc_toolchain(target_name) {
     cc = "clang"
     ld = "ld.lld"
+    ar = "llvm-ar"
 
     forward_variables_from(invoker,
                            [
@@ -149,6 +159,7 @@
   embedded_cc_toolchain(target_name) {
     cc = "clang"
     ld = "${invoker.tool_prefix}ld.bfd"
+    ar = "llvm-ar"
 
     forward_variables_from(invoker,
                            [
diff --git a/build/toolchain/gen_offset_size_header.py b/build/toolchain/gen_offset_size_header.py
new file mode 100755
index 0000000..f9e73cb
--- /dev/null
+++ b/build/toolchain/gen_offset_size_header.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+#
+# Copyright 2019 The Hafnium Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#!/usr/bin/env python
+"""Generate a header file with definitions of constants parsed from a binary."""
+
+import argparse
+import os
+import re
+import subprocess
+import sys
+
+HF_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
+BINUTILS_ROOT = os.path.join(HF_ROOT, "prebuilts", "linux-x64", "gcc", "bin")
+STRINGS = os.path.join(BINUTILS_ROOT, "aarch64-linux-android-strings")
+
+PROLOGUE = """
+/**
+ * This file was auto-generated by {}.
+ * Changes will be overwritten.
+ */
+
+#pragma once
+
+""".format(__file__)
+
+def main():
+	parser = argparse.ArgumentParser()
+	parser.add_argument("bin_file",
+		help="binary file to be parsed for definitions of constants")
+        parser.add_argument("out_file", help="output file");
+	args = parser.parse_args()
+
+	# Regex for finding definitions: <HAFNIUM_DEFINE name #value />
+	regex = re.compile(r'<HAFNIUM_DEFINE\s([A-Za-z0-9_]+)\s#([0-9]+) />')
+
+	# Extract strings from the input binary file.
+	stdout = subprocess.check_output([ STRINGS, args.bin_file ])
+	stdout = str(stdout).split(os.linesep)
+
+	with open(args.out_file, "w") as f:
+		f.write(PROLOGUE)
+		for line in stdout:
+			for match in regex.findall(line):
+				f.write("#define {} ({})\n".format(
+					match[0], match[1]))
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/build/toolchain/offset_size_header.gni b/build/toolchain/offset_size_header.gni
new file mode 100644
index 0000000..b409284
--- /dev/null
+++ b/build/toolchain/offset_size_header.gni
@@ -0,0 +1,84 @@
+# Copyright 2019 The Hafnium Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This template auto-generate a C header file with "#define" constants, e.g.
+# struct sizes and member offsets.
+#
+# It uses a trick similar to other projects, e.g. Linux, where the integer
+# constant is used as an immediate in an inline assembly block. The source file
+# is compiled and the constant extracted by a script which generates the header
+# file. For easy grep-ing, the constant is compiled into a '.ascii' string,
+# surrounded by magic strings, and extracted using the 'strings' binutils tool.
+#
+# To guarantee correctness, the same source file is compiled again as part
+# of the parent target but this time the declarations are converted to
+# static_asserts to check the values at its compile-time.
+template("offset_size_header") {
+  target_lib = "${target_name}__lib"
+  target_header = "${target_name}__header"
+
+  # Compile source files into binaries that contain strings with definitions
+  # of constants.
+  static_library(target_lib) {
+    forward_variables_from(invoker,
+                           [
+                             "sources",
+                             "deps",
+                             "test_only",
+                           ])
+    defines = [ "GENERATE_BINARY" ]
+
+    # Disable LTO to force emitting assembly.
+    cflags = [ "-fno-lto" ]
+  }
+
+  # Extract strings from the static library, parse definitions and generate
+  # a header file.
+  action(target_header) {
+    forward_variables_from(invoker, [ "test_only" ])
+    lib_file = "${target_out_dir}/${target_lib}.a"
+    out_file = "${root_gen_dir}/offset_size_header/${invoker.path}"
+
+    script = "//build/toolchain/gen_offset_size_header.py"
+    args = [
+      rebase_path(lib_file, root_build_dir),
+      rebase_path(out_file, root_build_dir),
+    ]
+    deps = [
+      ":$target_lib",
+    ]
+    outputs = [
+      out_file,
+    ]
+  }
+
+  # This source_set will be compiled into the target that depends on this one.
+  # This generates static_asserts which check the constants in the generated
+  # header against compile-time structs.
+  source_set(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "sources",
+                             "test_only",
+                           ])
+    cflags = [
+      "-include",
+      invoker.path,
+    ]
+    defines = [ "VERIFY_HEADER" ]
+    deps = [
+      ":$target_header",
+    ]
+  }
+}
diff --git a/inc/hf/offset_size_header.h b/inc/hf/offset_size_header.h
new file mode 100644
index 0000000..7d96950
--- /dev/null
+++ b/inc/hf/offset_size_header.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2019 The Hafnium Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This header file is intended for use by files compiled with the
+ * 'offset_size_header' build rule. See overview in 'offset_size_header.gni'.
+ */
+
+#pragma once
+
+#if (defined GENERATE_BINARY) && (defined VERIFY_HEADER)
+#error Only one action can be specified
+
+#elif defined GENERATE_BINARY
+
+/**
+ * File being compiled to generate binaries with definitions of constants.
+ */
+
+/**
+ * Emit a function with an embedded string in the format:
+ *     <HAFNIUM_DEFINE name #value />
+ * These will be recognized by a script that generates the header file.
+ */
+#define DEFINE(sym, val)                                    \
+	void gen_header__##sym(void)                        \
+	{                                                   \
+		__asm__ volatile(                           \
+			"\n"                                \
+			".ascii \"\\n<HAFNIUM_DEFINE " #sym \
+			" %0 />\\n\"\n"                     \
+			".align 8\n" /* Align epilogue */   \
+			:                                   \
+			: "i"(val));                        \
+	}
+
+#define DEFINE_SIZEOF(sym, type) DEFINE(sym, sizeof(type))
+#define DEFINE_OFFSETOF(sym, type, field) DEFINE(sym, offsetof(type, field))
+
+#elif defined VERIFY_HEADER
+
+/**
+ * File being compiled as part of the main build to check the values in
+ * the auto-generated header file (included using a command-line flag).
+ */
+
+#include "hf/static_assert.h"
+
+#define DEFINE_SIZEOF(sym, type)                                 \
+	void gen_header__##sym(void)                             \
+	{                                                        \
+		static_assert(sizeof(type) == sym,               \
+			      "Generated struct size mismatch"); \
+	}
+
+#define DEFINE_OFFSETOF(sym, type, field)                          \
+	void gen_header__##sym(void)                               \
+	{                                                          \
+		static_assert(offsetof(type, field) == sym,        \
+			      "Generated struct offset mismatch"); \
+	}
+
+#else
+#error No action specified
+#endif
diff --git a/src/arch/aarch64/hypervisor/BUILD.gn b/src/arch/aarch64/hypervisor/BUILD.gn
index c1ac799..0972dde 100644
--- a/src/arch/aarch64/hypervisor/BUILD.gn
+++ b/src/arch/aarch64/hypervisor/BUILD.gn
@@ -12,6 +12,15 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//build/toolchain/offset_size_header.gni")
+
+offset_size_header("offsets") {
+  sources = [
+    "offsets.c",
+  ]
+  path = "hf/arch/offsets.h"
+}
+
 # Hypervisor specific code.
 source_set("hypervisor") {
   public_configs = [ "//src/arch/aarch64:config" ]
@@ -24,13 +33,13 @@
   sources += [
     "debug_el1.c",
     "handler.c",
-    "offsets.c",
     "perfmon.c",
     "psci_handler.c",
     "sysregs.c",
   ]
 
   deps = [
+    ":offsets",
     "//src/arch/aarch64:arch",
     "//src/arch/aarch64:entry",
     "//src/arch/aarch64:smc",
diff --git a/src/arch/aarch64/hypervisor/exceptions.S b/src/arch/aarch64/hypervisor/exceptions.S
index e8e391b..b8a1fcd 100644
--- a/src/arch/aarch64/hypervisor/exceptions.S
+++ b/src/arch/aarch64/hypervisor/exceptions.S
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "offsets.h"
+#include "hf/arch/offsets.h"
 #include "exception_macros.S"
 
 /**
diff --git a/src/arch/aarch64/hypervisor/hypervisor_entry.S b/src/arch/aarch64/hypervisor/hypervisor_entry.S
index ce76aa5..5867805 100644
--- a/src/arch/aarch64/hypervisor/hypervisor_entry.S
+++ b/src/arch/aarch64/hypervisor/hypervisor_entry.S
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "offsets.h"
+#include "hf/arch/offsets.h"
 
 /**
  * Called only on first boot after the image has been relocated and BSS zeroed.
diff --git a/src/arch/aarch64/hypervisor/offsets.c b/src/arch/aarch64/hypervisor/offsets.c
index 1f32581..b99273c 100644
--- a/src/arch/aarch64/hypervisor/offsets.c
+++ b/src/arch/aarch64/hypervisor/offsets.c
@@ -14,24 +14,15 @@
  * limitations under the License.
  */
 
-#include "offsets.h"
-
 #include "hf/cpu.h"
-#include "hf/static_assert.h"
+#include "hf/offset_size_header.h"
 
-#define CHECK_OFFSET(name, type, field) \
-	CHECK_OFFSET_1(#name, name, offsetof(type, field))
-#define CHECK_OFFSET_1(name, actual, expected)               \
-	static_assert((actual) == (expected),                \
-		      "Offset " name " should be " #expected \
-		      " and not " #actual)
+DEFINE_OFFSETOF(CPU_ID, struct cpu, id)
+DEFINE_OFFSETOF(CPU_STACK_BOTTOM, struct cpu, stack_bottom)
+DEFINE_OFFSETOF(VCPU_REGS, struct vcpu, regs)
+DEFINE_OFFSETOF(VCPU_LAZY, struct vcpu, regs.lazy)
+DEFINE_OFFSETOF(VCPU_FREGS, struct vcpu, regs.fp)
 
-CHECK_OFFSET(CPU_ID, struct cpu, id);
-CHECK_OFFSET(CPU_STACK_BOTTOM, struct cpu, stack_bottom);
-CHECK_OFFSET(VCPU_REGS, struct vcpu, regs);
-CHECK_OFFSET(VCPU_LAZY, struct vcpu, regs.lazy);
-CHECK_OFFSET(VCPU_FREGS, struct vcpu, regs.fp);
-
-#ifdef VCPU_GIC
-CHECK_OFFSET(VCPU_GIC, struct vcpu, regs.gic);
+#if GIC_VERSION == 3 || GIC_VERSION == 4
+DEFINE_OFFSETOF(VCPU_GIC, struct vcpu, regs.gic)
 #endif
diff --git a/src/arch/aarch64/hypervisor/offsets.h b/src/arch/aarch64/hypervisor/offsets.h
deleted file mode 100644
index cc2621b..0000000
--- a/src/arch/aarch64/hypervisor/offsets.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2018 The Hafnium Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-/* These are checked in offset.c. */
-#define CPU_ID 0
-#define CPU_STACK_BOTTOM 8
-#define VCPU_REGS 32
-#define VCPU_LAZY (VCPU_REGS + 264)
-#define VCPU_FREGS (VCPU_LAZY + 280)
-
-#if GIC_VERSION == 3 || GIC_VERSION == 4
-#define VCPU_GIC (VCPU_FREGS + 528)
-#endif