Switch to new delocate tool.

Most importantly, this version of delocate works for ppc64le. It should
also work for x86-64, but will need significant testing to make sure
that it covers all the cases that the previous delocate.go covered.

It's less stringtastic than the old code, however the parser isn't as
nice as I would have liked. I thought that the reason we put up with
AT&T syntax with Intel is so that assembly syntax could be somewhat
consistent across platforms. At least for ppc64le, that does not appear
to be the case.

Change-Id: Ic7e3c6acc3803d19f2c3ff5620c5e39703d74212
Reviewed-on: https://boringssl-review.googlesource.com/16464
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d5f9ed9..7cc9100 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -191,6 +191,13 @@
 
 if(FIPS)
   add_definitions(-DBORINGSSL_FIPS)
+  if(FIPS_BREAK_TEST)
+    add_definitions("-DBORINGSSL_FIPS_BREAK_${FIPS_BREAK_TEST}=1")
+  endif()
+  # Delocate does not work for ASan and MSan builds.
+  if(NOT ASAN AND NOT MSAN)
+    set(FIPS_DELOCATE "1")
+  endif()
 endif()
 
 # CMake's iOS support uses Apple's multiple-architecture toolchain. It takes an
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
index 89a9ff2..f402caf 100644
--- a/crypto/CMakeLists.txt
+++ b/crypto/CMakeLists.txt
@@ -148,7 +148,7 @@
   thread_win.c
 )
 
-if(FIPS)
+if(FIPS_DELOCATE)
   SET_SOURCE_FILES_PROPERTIES(fipsmodule/bcm.o PROPERTIES EXTERNAL_OBJECT true)
   SET_SOURCE_FILES_PROPERTIES(fipsmodule/bcm.o PROPERTIES GENERATED true)
 
@@ -202,7 +202,7 @@
   ${CRYPTO_FIPS_OBJECTS}
 )
 
-if(FIPS)
+if(FIPS_DELOCATE)
   add_dependencies(crypto bcm_o_target)
 endif()
 
diff --git a/crypto/fipsmodule/CMakeLists.txt b/crypto/fipsmodule/CMakeLists.txt
index ab95fd6..553bf83 100644
--- a/crypto/fipsmodule/CMakeLists.txt
+++ b/crypto/fipsmodule/CMakeLists.txt
@@ -120,7 +120,7 @@
 perlasm(x86_64-mont.${ASM_EXT} bn/asm/x86_64-mont.pl)
 perlasm(x86-mont.${ASM_EXT} bn/asm/x86-mont.pl)
 
-if(FIPS)
+if(FIPS_DELOCATE)
   add_library(
     bcm_c_generated_asm
 
@@ -132,10 +132,6 @@
   set_target_properties(bcm_c_generated_asm PROPERTIES COMPILE_OPTIONS "-S")
   set_target_properties(bcm_c_generated_asm PROPERTIES POSITION_INDEPENDENT_CODE ON)
 
-  if(FIPS_BREAK_TEST)
-    target_compile_definitions(bcm_c_generated_asm PRIVATE "-DBORINGSSL_FIPS_BREAK_${FIPS_BREAK_TEST}=1")
-  endif()
-
   function(prepend_path values prefix output)
     set(result)
     foreach(value ${values})
@@ -148,8 +144,8 @@
 
   add_custom_command(
     OUTPUT bcm-delocated.S
-    COMMAND ${GO_EXECUTABLE} run util/fipstools/delocate.go util/fipstools/ar.go util/fipstools/const.go -a $<TARGET_FILE:bcm_c_generated_asm> -o ${CMAKE_CURRENT_BINARY_DIR}/bcm-delocated.S ${DELOCATE_ASM_ARGS}
-    DEPENDS bcm_c_generated_asm ${BCM_ASM_SOURCES} ${CMAKE_SOURCE_DIR}/util/fipstools/delocate.go ${CMAKE_SOURCE_DIR}/util/fipstools/ar.go ${CMAKE_SOURCE_DIR}/util/fipstools/const.go
+    COMMAND ${GO_EXECUTABLE} run util/fipstools/delocate.go util/fipstools/delocate.peg.go util/fipstools/ar.go util/fipstools/const.go -a $<TARGET_FILE:bcm_c_generated_asm> -o ${CMAKE_CURRENT_BINARY_DIR}/bcm-delocated.S ${DELOCATE_ASM_ARGS}
+    DEPENDS bcm_c_generated_asm ${BCM_ASM_SOURCES} ${CMAKE_SOURCE_DIR}/util/fipstools/delocate.go ${CMAKE_SOURCE_DIR}/util/fipstools/delocate.peg.go ${CMAKE_SOURCE_DIR}/util/fipstools/ar.go ${CMAKE_SOURCE_DIR}/util/fipstools/const.go
     WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
   )
 
diff --git a/crypto/fipsmodule/delocate.h b/crypto/fipsmodule/delocate.h
index a3b4721..0153a4e 100644
--- a/crypto/fipsmodule/delocate.h
+++ b/crypto/fipsmodule/delocate.h
@@ -20,7 +20,7 @@
 #include "../internal.h"
 
 
-#if defined(BORINGSSL_FIPS)
+#if defined(BORINGSSL_FIPS) && !defined(OPENSSL_ASAN) && !defined(OPENSSL_MSAN)
 #define DEFINE_BSS_GET(type, name)        \
   static type name __attribute__((used)); \
   type *name##_bss_get(void);
diff --git a/crypto/fipsmodule/rand/urandom.c b/crypto/fipsmodule/rand/urandom.c
index dcb19cd..2ea8730 100644
--- a/crypto/fipsmodule/rand/urandom.c
+++ b/crypto/fipsmodule/rand/urandom.c
@@ -234,11 +234,9 @@
   }
 }
 
-#if defined(USE_SYS_getrandom) && defined(__has_feature)
-#if __has_feature(memory_sanitizer)
+#if defined(USE_SYS_getrandom) && defined(OPENSSL_MSAN)
 void __msan_unpoison(void *, size_t);
 #endif
-#endif
 
 /* fill_with_entropy writes |len| bytes of entropy into |out|. It returns one
  * on success and zero on error. */
@@ -252,15 +250,13 @@
         r = syscall(SYS_getrandom, out, len, 0 /* no flags */);
       } while (r == -1 && errno == EINTR);
 
-#if defined(__has_feature)
-#if __has_feature(memory_sanitizer)
+#if defined(OPENSSL_MSAN)
       if (r > 0) {
         /* MSAN doesn't recognise |syscall| and thus doesn't notice that we
          * have initialised the output buffer. */
         __msan_unpoison(out, r);
       }
-#endif /* memory_sanitizer */
-#endif /*__has_feature */
+#endif /* OPENSSL_MSAN */
 
 #else /* USE_SYS_getrandom */
       abort();
diff --git a/crypto/test/malloc.cc b/crypto/test/malloc.cc
index bb1a6a6..bcd7974 100644
--- a/crypto/test/malloc.cc
+++ b/crypto/test/malloc.cc
@@ -14,12 +14,6 @@
 
 #include <openssl/base.h>
 
-#if defined(__has_feature)
-#if __has_feature(address_sanitizer) || __has_feature(memory_sanitizer)
-#define OPENSSL_ASAN_OR_MSAN
-#endif
-#endif
-
 #if defined(__GLIBC__) && !defined(__UCLIBC__)
 #define OPENSSL_GLIBC
 #endif
@@ -32,7 +26,8 @@
 // TODO(davidben): See if this and ASan's and MSan's interceptors can be made to
 // coexist.
 #if defined(__linux__) && defined(OPENSSL_GLIBC) && !defined(OPENSSL_ARM) && \
-    !defined(OPENSSL_AARCH64) && !defined(OPENSSL_ASAN_OR_MSAN)
+    !defined(OPENSSL_AARCH64) && !defined(OPENSSL_ASAN) &&                   \
+    !defined(OPENSSL_MSAN)
 
 #include <errno.h>
 #include <signal.h>
diff --git a/include/openssl/base.h b/include/openssl/base.h
index f00c563..0d4eab3 100644
--- a/include/openssl/base.h
+++ b/include/openssl/base.h
@@ -212,6 +212,9 @@
 #if __has_feature(address_sanitizer)
 #define OPENSSL_ASAN
 #endif
+#if __has_feature(memory_sanitizer)
+#define OPENSSL_MSAN
+#endif
 #endif
 
 /* CRYPTO_THREADID is a dummy value. */
diff --git a/util/fipstools/delocate.go b/util/fipstools/delocate.go
index 291e9c7..e0af6cb 100644
--- a/util/fipstools/delocate.go
+++ b/util/fipstools/delocate.go
@@ -12,535 +12,1176 @@
 // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
+//go:generate peg delocate.peg
+
 // delocate performs several transformations of textual assembly code. See
-// FIPS.md in this directory for an overview.
+// crypto/fipsmodule/FIPS.md for an overview.
 package main
 
 import (
-	"bufio"
-	"bytes"
+	"errors"
 	"flag"
 	"fmt"
+	"io/ioutil"
 	"os"
-	"path/filepath"
 	"sort"
 	"strconv"
 	"strings"
-	"unicode"
-	"unicode/utf8"
 )
 
-func main() {
-	// The .a file, if given, is expected to be an archive of textual
-	// assembly sources. That's odd, but CMake really wants to create
-	// archive files so it's the only way that we can make it work.
-	arInput := flag.String("a", "", "Path to a .a file containing assembly sources")
+// inputFile represents a textual assembly file.
+type inputFile struct {
+	path string
+	// index is a unique identifer given to this file. It's used for
+	// mapping local symbols.
+	index int
+	// isArchive indicates that the input should be processed as an ar
+	// file.
+	isArchive bool
+	// contents contains the contents of the file.
+	contents string
+	// ast points to the head of the syntax tree.
+	ast *node32
+}
 
-	outFile := flag.String("o", "", "Path to output assembly")
+type stringWriter interface {
+	WriteString(string) (int, error)
+}
 
-	flag.Parse()
+type processorType int
 
-	var lines []string
-	var err error
-	if len(*arInput) > 0 {
-		if lines, err = arLines(lines, *arInput); err != nil {
-			panic(err)
-		}
-	}
+const (
+	ppc64le processorType = iota + 1
+	x86_64
+)
 
-	for i, path := range flag.Args() {
-		if len(path) == 0 {
-			continue
-		}
+// delocation holds the state needed during a delocation operation.
+type delocation struct {
+	processor processorType
+	output    stringWriter
 
-		if lines, err = asLines(lines, path, i); err != nil {
-			panic(err)
-		}
-	}
+	// symbols is the set of symbols defined in the module.
+	symbols map[string]struct{}
+	// localEntrySymbols is the set of symbols with .localentry directives.
+	localEntrySymbols map[string]struct{}
+	// redirectors maps from out-call symbol name to the name of a
+	// redirector function for that symbol. E.g. “memcpy” ->
+	// “bcm_redirector_memcpy”.
+	redirectors map[string]string
+	// bssAccessorsNeeded maps from a BSS symbol name to the symbol that
+	// should be used to reference it. E.g. “P384_data_storage” ->
+	// “P384_data_storage”.
+	bssAccessorsNeeded map[string]string
+	// tocLoaders is a set of symbol names for which TOC helper functions
+	// are required. (ppc64le only.)
+	tocLoaders map[string]struct{}
+	// gotExternalsNeeded is a set of symbol names for which we need
+	// “delta” symbols: symbols that contain the offset from their location
+	// to the memory in question.
+	gotExternalsNeeded map[string]struct{}
 
-	symbols := definedSymbols(lines)
-	lines = transform(lines, symbols)
+	currentInput inputFile
+}
 
-	out, err := os.OpenFile(*outFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
-	if err != nil {
+func (d *delocation) contents(node *node32) string {
+	return d.currentInput.contents[node.begin:node.end]
+}
+
+// writeNode writes out an AST node.
+func (d *delocation) writeNode(node *node32) {
+	if _, err := d.output.WriteString(d.contents(node)); err != nil {
 		panic(err)
 	}
-	defer out.Close()
+}
 
-	for _, line := range lines {
-		out.WriteString(line)
-		out.WriteString("\n")
+func (d *delocation) writeCommentedNode(node *node32) {
+	line := d.contents(node)
+	if _, err := d.output.WriteString("# WAS " + strings.TrimSpace(line) + "\n"); err != nil {
+		panic(err)
 	}
 }
 
-func removeComment(line string) string {
-	if i := strings.Index(line, "#"); i != -1 {
-		return line[:i]
+func locateError(err error, with *node32, in inputFile) error {
+	posMap := translatePositions([]rune(in.contents), []int{int(with.begin)})
+	var line int
+	for _, pos := range posMap {
+		line = pos.line
 	}
-	return line
+
+	return fmt.Errorf("error while processing %q on line %d: %q", in.contents[with.begin:with.end], line, err)
 }
 
-// isSymbolDef returns detects whether line contains a (non-local) symbol
-// definition. If so, it returns the symbol and true. Otherwise it returns ""
-// and false.
-func isSymbolDef(line string) (string, bool) {
-	line = strings.TrimSpace(removeComment(line))
+func (d *delocation) processInput(input inputFile) (err error) {
+	d.currentInput = input
 
-	if len(line) > 0 && line[len(line)-1] == ':' && line[0] != '.' {
-		symbol := line[:len(line)-1]
-		if validSymbolName(symbol) {
-			return symbol, true
+	var origStatement *node32
+	defer func() {
+		if err := recover(); err != nil {
+			panic(locateError(fmt.Errorf("%s", err), origStatement, input))
 		}
-	}
+	}()
 
-	return "", false
-}
+	for statement := input.ast.up; statement != nil; statement = statement.next {
+		assertNodeType(statement, ruleStatement)
+		origStatement = statement
 
-// definedSymbols finds all (non-local) symbols from lines and returns a map
-// from symbol name to whether or not that symbol is global.
-func definedSymbols(lines []string) map[string]bool {
-	globalSymbols := make(map[string]struct{})
-	symbols := make(map[string]bool)
-
-	for _, line := range lines {
-		if len(line) == 0 {
+		node := skipWS(statement.up)
+		if node == nil {
+			d.writeNode(statement)
 			continue
 		}
 
-		if symbol, ok := isSymbolDef(line); ok {
-			_, isGlobal := globalSymbols[symbol]
-			symbols[symbol] = isGlobal
+		switch node.pegRule {
+		case ruleGlobalDirective, ruleComment, ruleLocationDirective:
+			d.writeNode(statement)
+		case ruleDirective:
+			statement, err = d.processDirective(statement, node.up)
+		case ruleLabelContainingDirective:
+			statement, err = d.processLabelContainingDirective(statement, node.up)
+		case ruleLabel:
+			statement, err = d.processLabel(statement, node.up)
+		case ruleInstruction:
+			switch d.processor {
+			case x86_64:
+				statement, err = d.processIntelInstruction(statement, node.up)
+			case ppc64le:
+				statement, err = d.processPPCInstruction(statement, node.up)
+			default:
+				panic("unknown processor")
+			}
+		default:
+			panic(fmt.Sprintf("unknown top-level statement type %q", rul3s[node.pegRule]))
 		}
 
-		parts := strings.Fields(strings.TrimSpace(line))
-		if parts[0] == ".globl" {
-			globalSymbols[parts[1]] = struct{}{}
+		if err != nil {
+			return locateError(err, origStatement, input)
 		}
 	}
 
-	return symbols
+	return nil
 }
 
-// threadLocalOffsetFunc describes a function that fetches the offset to symbol
-// in the thread-local space and writes it to the given target register.
-type threadLocalOffsetFunc struct {
-	target string
-	symbol string
-}
+func (d *delocation) processDirective(statement, directive *node32) (*node32, error) {
+	assertNodeType(directive, ruleDirectiveName)
+	directiveName := d.contents(directive)
 
-type lineSource struct {
-	lines  []string
-	lineNo int
-}
+	var args []string
+	forEachPath(directive, func(arg *node32) {
+		// If the argument is a quoted string, use the raw contents.
+		// (Note that this doesn't unescape the string, but that's not
+		// needed so far.
+		if arg.up != nil {
+			arg = arg.up
+			assertNodeType(arg, ruleQuotedArg)
+			if arg.up == nil {
+				args = append(args, "")
+				return
+			}
+			arg = arg.up
+			assertNodeType(arg, ruleQuotedText)
+		}
+		args = append(args, d.contents(arg))
+	}, ruleArgs, ruleArg)
 
-func (ls *lineSource) Next() (string, bool) {
-	if ls.lineNo == len(ls.lines) {
-		return "", false
+	switch directiveName {
+	case "comm", "lcomm":
+		if len(args) < 1 {
+			return nil, errors.New("comm directive has no arguments")
+		}
+		d.bssAccessorsNeeded[args[0]] = args[0]
+		d.writeNode(statement)
+
+	case "data":
+		// ASAN and some versions of MSAN are adding a .data section,
+		// and adding references to symbols within it to the code. We
+		// will have to work around this in the future.
+		return nil, errors.New(".data section found in module")
+
+	case "section":
+		section := args[0]
+
+		if section == ".data.rel.ro" {
+			// In a normal build, this is an indication of a
+			// problem but any references from the module to this
+			// section will result in a relocation and thus will
+			// break the integrity check. ASAN can generate these
+			// sections and so we will likely have to work around
+			// that in the future.
+			return nil, errors.New(".data.rel.ro section found in module")
+		}
+
+		sectionType, ok := sectionType(section)
+		if !ok {
+			// Unknown sections are permitted in order to be robust
+			// to different compiler modes.
+			d.writeNode(statement)
+			break
+		}
+
+		switch sectionType {
+		case ".rodata", ".text":
+			// Move .rodata to .text so it may be accessed without
+			// a relocation. GCC with -fmerge-constants will place
+			// strings into separate sections, so we move all
+			// sections named like .rodata. Also move .text.startup
+			// so the self-test function is also in the module.
+			d.writeCommentedNode(statement)
+			d.output.WriteString(".text\n")
+
+		case ".data":
+			// See above about .data
+			return nil, errors.New(".data section found in module")
+
+		case ".init_array", ".fini_array", ".ctors", ".dtors":
+			// init_array/ctors/dtors contains function
+			// pointers to constructor/destructor
+			// functions. These contain relocations, but
+			// they're in a different section anyway.
+			d.writeNode(statement)
+			break
+
+		case ".debug", ".note", ".toc":
+			d.writeNode(statement)
+			break
+
+		case ".bss":
+			d.writeNode(statement)
+			return d.handleBSS(statement)
+		}
+
+	default:
+		d.writeNode(statement)
 	}
 
-	ret := ls.lines[ls.lineNo]
-	ls.lineNo++
-	return ret, true
+	return statement, nil
 }
 
-func (ls *lineSource) Unread() {
-	ls.lineNo--
-}
+func (d *delocation) processLabelContainingDirective(statement, directive *node32) (*node32, error) {
+	// The symbols within directives need to be mapped so that local
+	// symbols in two different .s inputs don't collide.
+	changed := false
+	assertNodeType(directive, ruleLabelContainingDirectiveName)
+	name := d.contents(directive)
 
-func parseInstruction(line string) (instr string, args []string) {
-	line = strings.TrimSpace(line)
-	if len(line) == 0 || line[0] == '#' {
-		return "", nil
-	}
+	node := directive.next
+	assertNodeType(node, ruleWS)
 
-	idx := strings.IndexFunc(line, unicode.IsSpace)
-	if idx < 0 {
-		return line, nil
-	}
+	node = node.next
+	assertNodeType(node, ruleSymbolArgs)
 
-	instr = strings.TrimSpace(line[:idx])
-	line = strings.TrimSpace(line[idx:])
-	for len(line) > 0 {
-		var inQuote bool
-		var parens int
-	Loop:
-		for idx = 0; idx < len(line); idx++ {
-			if inQuote {
-				if line[idx] == '\\' {
-					if idx == len(line)-1 {
-						panic(fmt.Sprintf("could not parse %q", line))
-					}
-					idx++
-				} else {
-					inQuote = line[idx] != '"'
-				}
+	var args []string
+	for node = skipWS(node.up); node != nil; node = skipWS(node.next) {
+		assertNodeType(node, ruleSymbolArg)
+		arg := node.up
+		var mapped string
+
+		for term := arg; term != nil; term = term.next {
+			if term.pegRule != ruleLocalSymbol {
+				mapped += d.contents(term)
 				continue
 			}
-			switch line[idx] {
-			case '"':
-				inQuote = true
-			case '(':
-				parens++
-			case ')':
-				if parens == 0 {
-					panic(fmt.Sprintf("could not parse %q", line))
-				}
-				parens--
-			case ',':
-				if parens == 0 {
-					break Loop
-				}
-			case '#':
-				line = line[:idx]
-				break Loop
+
+			oldSymbol := d.contents(term)
+			newSymbol := d.mapLocalSymbol(oldSymbol)
+			if newSymbol != oldSymbol {
+				changed = true
 			}
+
+			mapped += newSymbol
 		}
 
-		if inQuote || parens > 0 {
-			panic(fmt.Sprintf("could not parse %q", line))
+		args = append(args, mapped)
+	}
+
+	if !changed {
+		d.writeNode(statement)
+	} else {
+		d.writeCommentedNode(statement)
+		d.output.WriteString("\t" + name + "\t" + strings.Join(args, ", ") + "\n")
+	}
+
+	if name == ".localentry" {
+		d.output.WriteString(localEntryName(args[0]) + ":\n")
+	}
+
+	return statement, nil
+}
+
+func (d *delocation) processLabel(statement, label *node32) (*node32, error) {
+	symbol := d.contents(label)
+
+	switch label.pegRule {
+	case ruleLocalLabel:
+		d.output.WriteString(symbol + ":\n")
+	case ruleLocalSymbol:
+		// symbols need to be mapped so that local symbols from two
+		// different .s inputs don't collide.
+		d.output.WriteString(d.mapLocalSymbol(symbol) + ":\n")
+	case ruleSymbolName:
+		d.output.WriteString(localTargetName(symbol) + ":\n")
+		d.writeNode(statement)
+	default:
+		return nil, fmt.Errorf("unknown label type %q", rul3s[label.pegRule])
+	}
+
+	return statement, nil
+}
+
+// instructionArgs collects all the arguments to an instruction.
+func instructionArgs(node *node32) (argNodes []*node32) {
+	for node = skipWS(node); node != nil; node = skipWS(node.next) {
+		assertNodeType(node, ruleInstructionArg)
+		argNodes = append(argNodes, node.up)
+	}
+
+	return argNodes
+}
+
+/* ppc64le
+
+[PABI]: “64-bit ELF v2 ABI Specification. Power Architechture.” March 21st,
+        2017
+
+(Also useful is “Power ISA Version 2.07 B”. Note that version three of that
+document is /not/ good as that's POWER9 specific.)
+
+ppc64le doesn't have IP-relative addressing and does a lot to work around this.
+Rather than reference a PLT and GOT direction, it has a single structure called
+the TOC (Table Of Contents). Within the TOC is the contents of .rodata, .data,
+.got, .plt, .bss, etc sections [PABI;3.3].
+
+A pointer to the TOC is maintained in r2 and the following pattern is used to
+load the address of an element into a register:
+
+  addis <address register>, 2, foo@toc@ha
+  addi <address register>, <address register>, foo@toc@l
+
+The “addis” instruction shifts a signed constant left 16 bits and adds the
+result to its second argument, saving the result in the first argument. The
+“addi” instruction does the same, but without shifting. Thus the “@toc@ha"
+suffix on a symbol means “the top 16 bits of the TOC offset” and “@toc@l” means
+“the bottom 16 bits of the offset”. However, note that both values are signed,
+thus offsets in the top half of a 64KB chunk will have an @ha value that's one
+greater than expected and a negative @l value.
+
+The TOC is specific to a “module” (basically an executable or shared object).
+This means that there's not a single TOC in a process and that r2 needs to
+change as control moves between modules. Thus functions have two entry points:
+the “global” entry point and the “local” entry point. Jumps from within the
+same module can use the local entry while jumps from other modules must use the
+global entry. The global entry establishes the correct value of r2 before
+running the function and the local entry skips that code.
+
+The global entry point for a function is defined by its label. The local entry
+is a power-of-two number of bytes from the global entry, set by the
+“.localentry” directive. (ppc64le instructions are always 32 bits, so an offset
+of 1 or 2 bytes is treated as an offset of zero.)
+
+In order to help the global entry code set r2 to point to the local TOC, r12 is
+set to the address of the global entry point when called [PABI;2.2.1.1]. Thus
+the global entry will typically use an addis+addi pair to add a known offset to
+r12 and store it in r2. For example:
+
+foo:
+  addis 2, 12, .TOC. - foo@ha
+  addi  2, 2,  .TOC. - foo@l
+
+(It's worth noting that the '@' operator binds very loosely, so the 3rd
+arguments parse as (.TOC. - foo)@ha and (.TOC. - foo)@l.)
+
+When calling a function, the compiler doesn't know whether that function is in
+the same module or not. Thus it doesn't know whether r12 needs to be set nor
+whether r2 will be clobbered on return. Rather than always assume the worst,
+the linker fixes stuff up once it knows that a call is going out of module:
+
+Firstly, calling, say, memcpy (which we assume to be in a different module)
+won't actually jump directly to memcpy, or even a PLT resolution function.
+It'll call a synthesised function that:
+  a) saves r2 in the caller's stack frame
+  b) loads the address of memcpy@PLT into r12
+  c) jumps to r12.
+
+As this synthesised function loads memcpy@PLT, a call to memcpy from the
+compiled code just references “memcpy” directly, not “memcpy@PLT”.
+
+Since it jumps directly to memcpy@PLT, it can't restore r2 on return. Thus
+calls must be followed by a nop. If the call ends up going out-of-module, the
+linker will rewrite that nop to load r2 from the stack.
+
+Speaking of the stack, the stack pointer is kept in r1 and there's a 288-byte
+red-zone. The format of the stack frame is defined [PABI;2.2.2] and must be
+followed as called functions will write into their parent's stack frame. For
+example, the synthesised out-of-module trampolines will save r2 24 bytes into
+the caller's frame and all non-leaf functions save the return address 16 bytes
+into the caller's frame.
+
+A final point worth noting: some RISC ISAs have r0 wired to zero: all reads
+result in zero and all writes are discarded. POWER does something a little like
+that, but r0 is only special in certain argument positions for certain
+instructions. You just have to read the manual to know which they are.
+
+
+Delocation is easier than Intel because there's just TOC references, but it's
+also harder because there's no IP-relative addressing.
+
+Jumps are IP-relative however, and have a 24-bit immediate value. So we can
+jump to functions that set a register to the needed value. (r3 is the
+return-value register and so that's what is generally used here.) */
+
+// isPPC64LEAPair recognises an addis+addi pair that's adding the offset of
+// source to relative and writing the result to target.
+func (d *delocation) isPPC64LEAPair(statement *node32) (target, source, relative string, ok bool) {
+	instruction := skipWS(statement.up).up
+	assertNodeType(instruction, ruleInstructionName)
+	name1 := d.contents(instruction)
+	args1 := instructionArgs(instruction.next)
+
+	statement = statement.next
+	instruction = skipWS(statement.up).up
+	assertNodeType(instruction, ruleInstructionName)
+	name2 := d.contents(instruction)
+	args2 := instructionArgs(instruction.next)
+
+	if name1 != "addis" ||
+		len(args1) != 3 ||
+		name2 != "addi" ||
+		len(args2) != 3 {
+		return "", "", "", false
+	}
+
+	target = d.contents(args1[0])
+	relative = d.contents(args1[1])
+	source1 := d.contents(args1[2])
+	source2 := d.contents(args2[2])
+
+	if !strings.HasSuffix(source1, "@ha") ||
+		!strings.HasSuffix(source2, "@l") ||
+		source1[:len(source1)-3] != source2[:len(source2)-2] ||
+		d.contents(args2[0]) != target ||
+		d.contents(args2[1]) != target {
+		return "", "", "", false
+	}
+
+	source = source1[:len(source1)-3]
+	ok = true
+	return
+}
+
+// establishTOC writes the global entry prelude for a function. The standard
+// prelude involves relocations so this version moves the relocation outside
+// the integrity-checked area.
+func establishTOC(w stringWriter) {
+	w.WriteString("999:\n")
+	w.WriteString("\taddis 2, 12, .LBORINGSSL_external_toc-999b@ha\n")
+	w.WriteString("\taddi 2, 2, .LBORINGSSL_external_toc-999b@l\n")
+	w.WriteString("\tld 12, 0(2)\n")
+	w.WriteString("\tadd 2, 2, 12\n")
+}
+
+// loadTOCFuncName returns the name of a synthesized function that sets r3 to
+// the value of “symbol”.
+func loadTOCFuncName(symbol string) string {
+	return ".Lbcm_loadtoc_" + strings.Replace(symbol, ".", "_dot_", -1)
+}
+
+func (d *delocation) loadFromTOC(w stringWriter, symbol, dest string) wrapperFunc {
+	d.tocLoaders[symbol] = struct{}{}
+	return func(k func()) {
+		w.WriteString("\taddi 1, 1, -288\n")   // Clear the red zone.
+		w.WriteString("\tmflr " + dest + "\n") // Stash the link register.
+		w.WriteString("\tstd " + dest + ", -8(1)\n")
+		// The TOC loader will use r3, so stash it if necessary.
+		if dest != "3" {
+			w.WriteString("\tstd 3, -16(1)\n")
 		}
 
-		args = append(args, strings.TrimSpace(line[:idx]))
-		if idx < len(line) {
-			// Skip the comma.
-			line = line[idx+1:]
-		} else {
-			line = line[idx:]
+		// Because loadTOCFuncName returns a “.L” name, we don't need a
+		// nop after this call.
+		w.WriteString("\tbl " + loadTOCFuncName(symbol) + "\n")
+
+		// Cycle registers around. We need r3 -> destReg, -8(1) ->
+		// lr and, optionally, -16(1) -> r3.
+		w.WriteString("\tstd 3, -24(1)\n")
+		w.WriteString("\tld 3, -8(1)\n")
+		w.WriteString("\tmtlr 3\n")
+		w.WriteString("\tld " + dest + ", -24(1)\n")
+		if dest != "3" {
+			w.WriteString("\tld 3, -16(1)\n")
 		}
+		w.WriteString("\taddi 1, 1, 288\n")
+
+		k()
+	}
+}
+
+func (d *delocation) parseMemRef(memRef *node32) (symbol, offset, section string, didChange, symbolIsLocal bool, nextRef *node32) {
+	if memRef.pegRule != ruleSymbolRef {
+		return "", "", "", false, false, memRef
+	}
+
+	symRef := memRef.up
+	nextRef = memRef.next
+
+	if symRef.pegRule == ruleOffset {
+		offset = d.contents(symRef)
+		if offset[0] != '+' && offset[0] != '-' {
+			offset = "+" + offset
+		}
+		symRef = symRef.next
+	}
+
+	symbol = d.contents(symRef)
+	if symRef.pegRule == ruleLocalSymbol {
+		symbolIsLocal = true
+		mapped := d.mapLocalSymbol(symbol)
+		if mapped != symbol {
+			symbol = mapped
+			didChange = true
+		}
+	}
+
+	symRef = symRef.next
+	if symRef != nil && symRef.pegRule == ruleOffset {
+		if len(offset) != 0 {
+			panic("symref has two offsets")
+		}
+		offset = d.contents(symRef)
+		symRef = symRef.next
+	}
+
+	if symRef != nil {
+		assertNodeType(symRef, ruleSection)
+		section = d.contents(symRef)
+		symRef = symRef.next
+	}
+
+	if symRef != nil {
+		panic(fmt.Sprintf("unexpected token in SymbolRef: %q", rul3s[symRef.pegRule]))
 	}
 
 	return
 }
 
-// transform performs a number of transformations on the given assembly code.
-// See FIPS.md in the current directory for an overview.
-func transform(lines []string, symbols map[string]bool) (ret []string) {
-	ret = append(ret, ".text", "BORINGSSL_bcm_text_start:")
+func (d *delocation) processPPCInstruction(statement, instruction *node32) (*node32, error) {
+	assertNodeType(instruction, ruleInstructionName)
+	instructionName := d.contents(instruction)
+	isBranch := instructionName[0] == 'b'
 
-	// redirectors maps from out-call symbol name to the name of a
-	// redirector function for that symbol.
-	redirectors := make(map[string]string)
+	argNodes := instructionArgs(instruction.next)
 
-	// ia32capAddrDeltaNeeded is true iff OPENSSL_ia32cap_addr_delta has
-	// been referenced and thus needs to be emitted outside the module.
-	ia32capAddrDeltaNeeded := false
+	var wrappers wrapperStack
+	var args []string
+	changed := false
 
-	// ia32capGetNeeded is true iff OPENSSL_ia32cap_get has been referenced
-	// and thus needs to be emitted outside the module.
-	ia32capGetNeeded := false
+Args:
+	for i, arg := range argNodes {
+		fullArg := arg
+		isIndirect := false
 
-	// bssAccessorsNeeded maps the names of BSS variables for which
-	// accessor functions need to be emitted outside of the module, to the
-	// BSS symbols they point to. For example, “EVP_sha256_once” could map
-	// to “.LEVP_sha256_once_local_target” or “EVP_sha256_once” (if .comm
-	// was used).
-	bssAccessorsNeeded := make(map[string]string)
-
-	// threadLocalOffsets records the accessor functions needed for getting
-	// offsets in the thread-local storage.
-	threadLocalOffsets := make(map[string]threadLocalOffsetFunc)
-
-	// gotpcrelExternalsNeeded is the set of symbols which are accessed via
-	// the GOT and will need external relocations emitted.
-	gotpcrelExternalsNeeded := make(map[string]struct{})
-
-	source := &lineSource{lines: lines}
-
-	for {
-		line, ok := source.Next()
-		if !ok {
-			break
+		if arg.pegRule == ruleIndirectionIndicator {
+			arg = arg.next
+			isIndirect = true
 		}
 
-		orig := line
+		switch arg.pegRule {
+		case ruleRegisterOrConstant, ruleLocalLabelRef:
+			args = append(args, d.contents(fullArg))
 
-		if strings.Contains(line, "OPENSSL_ia32cap_get@PLT") {
-			ia32capGetNeeded = true
-		}
+		case ruleTOCRefLow:
+			return nil, errors.New("Found low TOC reference outside preamble pattern")
 
-		line = strings.Replace(line, "@PLT", "", -1)
-
-		instr, args := parseInstruction(line)
-		if len(instr) == 0 {
-			ret = append(ret, line)
-			continue
-		}
-
-		switch instr {
-		case "call", "callq", "jmp", "jne", "jb", "jz", "jnz", "ja":
-			target := args[0]
-			// indirect via register or local label
-			if strings.HasPrefix(target, "*") || isLocalLabel(target) {
-				ret = append(ret, line)
-				continue
-			}
-
-			if strings.HasSuffix(target, "_bss_get") || target == "OPENSSL_ia32cap_get" {
-				// reference to a synthesised function. Don't
-				// indirect it.
-				ret = append(ret, line)
-				continue
-			}
-
-			if isGlobal, ok := symbols[target]; ok {
-				newTarget := target
-				if isGlobal {
-					newTarget = localTargetName(target)
-				}
-				ret = append(ret, fmt.Sprintf("\t%s %s", instr, newTarget))
-				continue
-			}
-
-			redirectorName := "bcm_redirector_" + target
-			ret = append(ret, fmt.Sprintf("\t%s %s", instr, redirectorName))
-			redirectors[redirectorName] = target
-			continue
-
-		case "pushq":
-			target := args[0]
-			if strings.HasSuffix(target, "@GOTPCREL(%rip)") {
-				target = target[:len(target)-15]
-				if !symbols[target] {
-					panic(fmt.Sprintf("Reference to unknown symbol on line %d: %s", source.lineNo, line))
-				}
-
-				ret = append(ret, "\tpushq %rax")
-				ret = append(ret, "\tleaq "+localTargetName(target)+"(%rip), %rax")
-				ret = append(ret, "\txchg %rax, (%rsp)")
-				continue
-			}
-
-			ret = append(ret, line)
-			continue
-
-		case "leaq", "vmovq", "movq", "cmpq", "cmovneq", "cmoveq":
-			if instr == "movq" && strings.Contains(line, "@GOTTPOFF(%rip)") {
-				// GOTTPOFF are offsets into the thread-local
-				// storage that are stored in the GOT. We have
-				// to move these relocations out of the module,
-				// but do not know whether rax is live at this
-				// point. Thus a normal function call might
-				// clobber a register and so we synthesize
-				// different functions for writing to each
-				// target register.
-				//
-				// (BoringSSL itself does not use __thread
-				// variables, but ASAN and MSAN may add these
-				// references for their bookkeeping.)
-				targetRegister := args[1][1:]
-				symbol := strings.SplitN(args[0], "@", 2)[0]
-				functionName := fmt.Sprintf("BORINGSSL_bcm_tpoff_to_%s_for_%s", targetRegister, symbol)
-				threadLocalOffsets[functionName] = threadLocalOffsetFunc{target: targetRegister, symbol: symbol}
-				ret = append(ret, "leaq -128(%rsp), %rsp") // Clear the red zone.
-				ret = append(ret, "\tcallq "+functionName+"\n")
-				ret = append(ret, "leaq 128(%rsp), %rsp")
-				continue
-			}
-
-			target := args[0]
-			invertedCondition := ""
-
-			if strings.HasSuffix(target, "(%rip)") {
-				target = target[:len(target)-6]
-				if isGlobal := symbols[target]; isGlobal {
-					line = strings.Replace(line, target, localTargetName(target), 1)
-				}
-
-				if strings.Contains(line, "@GOTPCREL") && (instr == "movq" || instr == "vmovq" || instr == "cmoveq" || instr == "cmovneq") {
-					line = strings.Replace(line, "@GOTPCREL", "", -1)
-					target = strings.Replace(target, "@GOTPCREL", "", -1)
-					var useGOT bool
-
-					if isGlobal := symbols[target]; isGlobal {
-						line = strings.Replace(line, target, localTargetName(target), 1)
-						target = localTargetName(target)
-					} else if target != "OPENSSL_ia32cap_P" && !strings.HasPrefix(target, "BORINGSSL_bcm_") {
-						// If the symbol is defined external to libcrypto.a,
-						// we need to use the GOT to avoid a runtime
-						// relocation.
-						useGOT = true
-					}
-
-					switch instr {
-					case "cmoveq":
-						invertedCondition = "ne"
-					case "cmovneq":
-						invertedCondition = "e"
-					}
-
-					if len(invertedCondition) > 0 {
-						ret = append(ret, "\t# Was "+orig)
-						ret = append(ret, "\tj"+invertedCondition+" 1f")
-					}
-
-					destination := args[1]
-					if useGOT {
-						if !strings.HasPrefix(destination, "%r") || destination == "%rsp" {
-							// If it comes up, we can support %xmm* or memory references by
-							// picking a spare register, but we must take care not to use a
-							// register referenced in the destination.
-							panic("destination must be a standard 64-bit register")
-						}
-
-						ret = append(ret, "leaq -128(%rsp), %rsp") // Clear the red zone.
-						ret = append(ret, "pushf")
-						ret = append(ret, fmt.Sprintf("leaq %s_GOTPCREL_external(%%rip), %s", target, destination))
-						ret = append(ret, fmt.Sprintf("addq (%s), %s", destination, destination))
-						ret = append(ret, fmt.Sprintf("movq (%s), %s", destination, destination))
-						ret = append(ret, "popf")
-						ret = append(ret, "leaq 128(%rsp), %rsp")
-
-						gotpcrelExternalsNeeded[target] = struct{}{}
-						continue
-					}
-
-					if strings.HasPrefix(destination, "%xmm") {
-						if instr != "movq" && instr != "vmovq" {
-							panic("unhandled: " + orig)
-						}
-
-						// MOV can target XMM
-						// registers, but LEA cannot.
-						ret = append(ret, "leaq -128(%rsp), %rsp") // Clear the red zone.
-						ret = append(ret, "pushq %rax")
-						ret = append(ret, "leaq "+target+"(%rip), %rax")
-						ret = append(ret, "movq %rax, "+destination)
-						ret = append(ret, "popq %rax")
-						ret = append(ret, "leaq 128(%rsp), %rsp")
-
-						continue
-					}
-
-					// A movq from the GOT is equivalent to a leaq. This symbol
-					// is defined in libcrypto, so we can reference it directly.
-					line = strings.Replace(line, instr, "leaq", 1)
-					instr = "leaq"
-				}
-
-				if target == "OPENSSL_ia32cap_P" {
-					if instr != "leaq" {
-						panic("reference to OPENSSL_ia32cap_P needs to be changed to go through leaq or GOTPCREL")
-					}
-					if args[1][0] != '%' {
-						panic("reference to OPENSSL_ia32cap_P must target a register.")
-					}
-
-					// We assume pushfq is safe, after
-					// clearing the red zone, because any
-					// signals will be delivered using
-					// %rsp. Thus perlasm and
-					// compiler-generated code must not use
-					// %rsp as a general-purpose register.
-					//
-					// TODO(davidben): This messes up CFI
-					// for a small window if %rsp is the CFI
-					// register.
-					ia32capAddrDeltaNeeded = true
-					ret = append(ret, "leaq -128(%rsp), %rsp") // Clear the red zone.
-					ret = append(ret, "pushfq")
-					ret = append(ret, fmt.Sprintf("leaq OPENSSL_ia32cap_addr_delta(%%rip), %s", args[1]))
-					ret = append(ret, fmt.Sprintf("addq (%s), %s", args[1], args[1]))
-					ret = append(ret, "popfq")
-					ret = append(ret, "leaq 128(%rsp), %rsp")
-					continue
-				}
-			}
-
-			ret = append(ret, line)
-			if len(invertedCondition) > 0 {
-				ret = append(ret, "1:")
-			}
-			continue
-
-		case ".comm":
-			name := args[0]
-			bssAccessorsNeeded[name] = name
-			ret = append(ret, line)
-
-		case ".section":
-			section := strings.Trim(args[0], "\"")
-			if section == ".data.rel.ro" {
-				// In a normal build, this is an indication of
-				// a problem but any references from the module
-				// to this section will result in a relocation
-				// and thus will break the integrity check.
-				// However, ASAN can generate these sections
-				// and so we cannot forbid them.
-				ret = append(ret, line)
-				continue
-			}
-
-			sectionType, ok := sectionType(section)
+		case ruleTOCRefHigh:
+			target, _, relative, ok := d.isPPC64LEAPair(statement)
 			if !ok {
-				panic(fmt.Sprintf("unknown section %q on line %d", section, source.lineNo))
+				return nil, errors.New("Found high TOC reference outside preamble pattern")
 			}
 
-			switch sectionType {
-			case ".rodata", ".text":
-				// Move .rodata to .text so it may be accessed
-				// without a relocation. GCC with
-				// -fmerge-constants will place strings into
-				// separate sections, so we move all sections
-				// named like .rodata. Also move .text.startup
-				// so the self-test function is also in the
-				// module.
-				ret = append(ret, ".text  # "+section)
+			if relative != "12" {
+				return nil, fmt.Errorf("preamble is relative to %q, not r12", relative)
+			}
 
-			case ".data":
-				panic(fmt.Sprintf("bad section %q on line %d", args[0], source.lineNo))
+			if target != "2" {
+				return nil, fmt.Errorf("preamble is setting %q, not r2", target)
+			}
 
-			case ".init_array", ".fini_array", ".ctors", ".dtors":
-				// init_array/ctors/dtors contains function
-				// pointers to constructor/destructor
-				// functions. These contain relocations, but
-				// they're in a different section anyway.
-				ret = append(ret, line)
+			statement = statement.next
+			establishTOC(d.output)
+			instructionName = ""
+			changed = true
+			break Args
 
-			case ".debug", ".note":
-				ret = append(ret, line)
+		case ruleMemoryRef:
+			symbol, offset, section, didChange, symbolIsLocal, memRef := d.parseMemRef(arg.up)
+			changed = didChange
 
-			case ".bss":
-				ret = append(ret, line)
+			if len(symbol) > 0 {
+				if _, localEntrySymbol := d.localEntrySymbols[symbol]; localEntrySymbol && isBranch {
+					symbol = localEntryName(symbol)
+					changed = true
+				} else if _, knownSymbol := d.symbols[symbol]; knownSymbol {
+					symbol = localTargetName(symbol)
+					changed = true
+				} else if !symbolIsLocal && !isSynthesized(symbol) && len(section) == 0 {
+					changed = true
+					d.redirectors[symbol] = redirectorName(symbol)
+					symbol = redirectorName(symbol)
+				}
+			}
 
-				var accessors map[string]string
-				accessors, ret = handleBSSSection(ret, source)
-				for accessor, name := range accessors {
-					bssAccessorsNeeded[accessor] = name
+			switch section {
+			case "":
+
+			case "tls":
+				// This section identifier just tells the
+				// assembler to use r13, the pointer to the
+				// thread-local data [PABI;3.7.3.3].
+
+			case "toc@ha":
+				// Delete toc@ha instructions. Per
+				// [PABI;3.6.3], the linker is allowed to erase
+				// toc@ha instructions. We take advantage of
+				// this by unconditionally erasing the toc@ha
+				// instructions and doing the full lookup when
+				// processing toc@l.
+				if instructionName != "addis" || len(argNodes) != 3 || i != 2 || args[1] != "2" {
+					return nil, errors.New("can't process toc@ha reference")
+				}
+				changed = true
+				instructionName = ""
+				break Args
+
+			case "toc@l":
+				// Per [PAB;3.6.3], this instruction must take
+				// as input a register which was the output of
+				// a toc@ha computation and compute the actual
+				// address of some symbol. The toc@ha
+				// computation was elided, so we ignore that
+				// input register and compute the address
+				// directly.
+				changed = true
+
+				// For all supported toc@l instructions, the
+				// destination register is the first argument.
+				destReg := args[0]
+
+				wrappers = append(wrappers, d.loadFromTOC(d.output, symbol, destReg))
+				switch instructionName {
+				case "addi":
+					// The original instruction was:
+					//   addi destReg, tocHaReg, symbol@toc@l+offset
+					//
+					// All that is left is adding the offset, if any.
+					instructionName = ""
+					if len(offset) != 0 {
+						wrappers = append(wrappers, func(k func()) {
+							d.output.WriteString("\taddi " + destReg + ", " + destReg + ", " + offset + "\n")
+						})
+					}
+				case "ld", "lhz", "lwz":
+					// The original instruction was:
+					//   l?? destReg, symbol@toc@l+offset(tocHaReg)
+					//
+					// We transform that into the
+					// equivalent dereference of destReg:
+					//   l?? destReg, offset(destReg)
+					origInstructionName := instructionName
+					instructionName = ""
+
+					assertNodeType(memRef, ruleBaseIndexScale)
+					assertNodeType(memRef.up, ruleRegisterOrConstant)
+					if memRef.next != nil || memRef.up.next != nil {
+						return nil, errors.New("expected single register in BaseIndexScale for ld argument")
+					}
+
+					wrappers = append(wrappers, func(k func()) {
+						fixedOffset := offset
+						if len(fixedOffset) == 0 {
+							fixedOffset = "0"
+						}
+						d.output.WriteString("\t" + origInstructionName + " " + destReg + ", " + fixedOffset + "(" + destReg + ")\n")
+					})
+				default:
+					return nil, fmt.Errorf("can't process TOC argument to %q", instructionName)
 				}
 
 			default:
-				panic(fmt.Sprintf("unknown section %q on line %d", section, source.lineNo))
+				return nil, fmt.Errorf("Unknown section type %q", section)
 			}
 
+			argStr := ""
+			if isIndirect {
+				argStr += "*"
+			}
+			argStr += symbol
+			if len(offset) > 0 {
+				argStr += offset
+			}
+			if len(section) > 0 {
+				argStr += "@"
+				argStr += section
+			}
+
+			for ; memRef != nil; memRef = memRef.next {
+				argStr += d.contents(memRef)
+			}
+
+			args = append(args, argStr)
+
 		default:
-			if symbol, ok := isSymbolDef(line); ok {
-				if isGlobal := symbols[symbol]; isGlobal {
-					ret = append(ret, localTargetName(symbol)+":")
-				}
-			}
-
-			ret = append(ret, line)
+			panic(fmt.Sprintf("unknown instruction argument type %q", rul3s[arg.pegRule]))
 		}
 	}
 
-	ret = append(ret, ".text")
-	ret = append(ret, "BORINGSSL_bcm_text_end:")
+	if changed {
+		d.writeCommentedNode(statement)
 
-	// Emit redirector functions. Each is a single JMP instruction.
+		var replacement string
+		if len(instructionName) > 0 {
+			replacement = "\t" + instructionName + "\t" + strings.Join(args, ", ") + "\n"
+		}
+
+		wrappers.do(func() {
+			d.output.WriteString(replacement)
+		})
+	} else {
+		d.writeNode(statement)
+	}
+
+	return statement, nil
+}
+
+/* Intel */
+
+type instructionType int
+
+const (
+	instrPush instructionType = iota
+	instrMove
+	instrJump
+	instrConditionalMove
+	instrOther
+)
+
+func classifyInstruction(instr string, args []*node32) instructionType {
+	switch instr {
+	case "push", "pushq":
+		if len(args) == 1 {
+			return instrPush
+		}
+
+	case "mov", "movq", "vmovq":
+		if len(args) == 2 {
+			return instrMove
+		}
+
+	case "cmovneq", "cmoveq":
+		if len(args) == 2 {
+			return instrConditionalMove
+		}
+
+	case "call", "callq", "jmp", "jo", "jno", "js", "jns", "je", "jz", "jne", "jnz", "jb", "jnae", "jc", "jnb", "jae", "jnc", "jbe", "jna", "ja", "jnbe", "jl", "jnge", "jge", "jnl", "jle", "jng", "jg", "jnle", "jp", "jpe", "jnp", "jpo":
+		if len(args) == 1 {
+			return instrJump
+		}
+	}
+
+	return instrOther
+}
+
+func push(w stringWriter) wrapperFunc {
+	return func(k func()) {
+		w.WriteString("\tpushq %rax\n")
+		k()
+		w.WriteString("\txchg %rax, (%rsp)\n")
+	}
+}
+
+func (d *delocation) loadFromGOT(w stringWriter, destination, symbol, section string, redzoneCleared bool) wrapperFunc {
+	d.gotExternalsNeeded[symbol+"@"+section] = struct{}{}
+
+	return func(k func()) {
+		if !redzoneCleared {
+			w.WriteString("\tleaq -128(%rsp), %rsp\n") // Clear the red zone.
+		}
+		w.WriteString("\tpushf\n")
+		w.WriteString(fmt.Sprintf("\tleaq %s_%s_external(%%rip), %s\n", symbol, section, destination))
+		w.WriteString(fmt.Sprintf("\taddq (%s), %s\n", destination, destination))
+		w.WriteString(fmt.Sprintf("\tmovq (%s), %s\n", destination, destination))
+		w.WriteString("\tpopf\n")
+		if !redzoneCleared {
+			w.WriteString("\tleaq\t128(%rsp), %rsp\n")
+		}
+	}
+}
+
+func saveRegister(w stringWriter) wrapperFunc {
+	return func(k func()) {
+		w.WriteString("\tleaq -128(%rsp), %rsp\n") // Clear the red zone.
+		w.WriteString("\tpushq %rax\n")
+		k()
+		w.WriteString("\tpopq %rax\n")
+		w.WriteString("\tleaq 128(%rsp), %rsp\n")
+	}
+}
+
+func moveTo(w stringWriter, target string) wrapperFunc {
+	return func(k func()) {
+		k()
+		w.WriteString("\tmovq %rax, " + target + "\n")
+	}
+}
+
+func isValidLEATarget(reg string) bool {
+	return !strings.HasPrefix(reg, "%xmm") && !strings.HasPrefix(reg, "%ymm") && !strings.HasPrefix(reg, "%zmm")
+}
+
+func undoConditionalMove(w stringWriter, instr string) wrapperFunc {
+	var invertedCondition string
+
+	switch instr {
+	case "cmoveq":
+		invertedCondition = "ne"
+	case "cmovneq":
+		invertedCondition = "e"
+	default:
+		panic(fmt.Sprintf("don't know how to handle conditional move instruction %q", instr))
+	}
+
+	return func(k func()) {
+		w.WriteString("\tj" + invertedCondition + " 999f\n")
+		k()
+		w.WriteString("999:\n")
+	}
+}
+
+func (d *delocation) isRIPRelative(node *node32) bool {
+	return node != nil && node.pegRule == ruleBaseIndexScale && d.contents(node) == "(%rip)"
+}
+
+func (d *delocation) processIntelInstruction(statement, instruction *node32) (*node32, error) {
+	assertNodeType(instruction, ruleInstructionName)
+	instructionName := d.contents(instruction)
+
+	argNodes := instructionArgs(instruction.next)
+
+	var wrappers wrapperStack
+	var args []string
+	changed := false
+
+Args:
+	for i, arg := range argNodes {
+		fullArg := arg
+		isIndirect := false
+
+		if arg.pegRule == ruleIndirectionIndicator {
+			arg = arg.next
+			isIndirect = true
+		}
+
+		switch arg.pegRule {
+		case ruleRegisterOrConstant, ruleLocalLabelRef:
+			args = append(args, d.contents(fullArg))
+
+		case ruleMemoryRef:
+			symbol, offset, section, didChange, symbolIsLocal, memRef := d.parseMemRef(arg.up)
+			changed = didChange
+
+			if symbol == "OPENSSL_ia32cap_P" {
+				var ok bool
+				if section == "GOTPCREL" {
+					ok = instructionName == "movq"
+				} else if section == "" {
+					ok = instructionName == "leaq"
+				}
+
+				if !ok {
+					return nil, fmt.Errorf("instruction %q referenced OPENSSL_ia32cap_P in section %q, should be a movq from GOTPCREL or a direct leaq", instructionName, section)
+				}
+
+				if i != 0 || len(argNodes) != 2 || !d.isRIPRelative(memRef) || len(offset) > 0 {
+					return nil, fmt.Errorf("invalid OPENSSL_ia32cap_P reference in instruction %q", instructionName)
+				}
+
+				target := argNodes[1]
+				assertNodeType(target, ruleRegisterOrConstant)
+				reg := d.contents(target)
+
+				if !strings.HasPrefix(reg, "%r") {
+					return nil, fmt.Errorf("tried to load OPENSSL_ia32cap_P into %q, which is not a standard register.", reg)
+				}
+
+				changed = true
+				wrappers = append(wrappers, func(k func()) {
+					d.output.WriteString("\tleaq\t-128(%rsp), %rsp\n") // Clear the red zone.
+					d.output.WriteString("\tpushfq\n")
+					d.output.WriteString("\tleaq\tOPENSSL_ia32cap_addr_delta(%rip), " + reg + "\n")
+					d.output.WriteString("\taddq\t(" + reg + "), " + reg + "\n")
+					d.output.WriteString("\tpopfq\n")
+					d.output.WriteString("\tleaq\t128(%rsp), %rsp\n")
+				})
+
+				break Args
+			}
+
+			switch section {
+			case "":
+				if _, knownSymbol := d.symbols[symbol]; knownSymbol {
+					symbol = localTargetName(symbol)
+					changed = true
+				}
+
+			case "PLT":
+				if classifyInstruction(instructionName, argNodes) != instrJump {
+					return nil, fmt.Errorf("Cannot rewrite PLT reference for non-jump instruction %q", instructionName)
+				}
+
+				if _, knownSymbol := d.symbols[symbol]; knownSymbol {
+					symbol = localTargetName(symbol)
+					changed = true
+				} else if !symbolIsLocal && !isSynthesized(symbol) {
+					// Unknown symbol via PLT is an
+					// out-call from the module, e.g.
+					// memcpy.
+					d.redirectors[symbol+"@"+section] = redirectorName(symbol)
+					symbol = redirectorName(symbol)
+				}
+
+				changed = true
+
+			case "GOTPCREL":
+				if len(offset) > 0 {
+					return nil, errors.New("loading from GOT with offset is unsupported")
+				}
+				if i != 0 {
+					return nil, errors.New("GOT access must be source operand")
+				}
+				if !d.isRIPRelative(memRef) {
+					return nil, errors.New("GOT access must be IP-relative")
+				}
+
+				useGOT := false
+				if _, knownSymbol := d.symbols[symbol]; knownSymbol {
+					symbol = localTargetName(symbol)
+					changed = true
+				} else if !isSynthesized(symbol) {
+					useGOT = true
+				}
+
+				// Reduce the instruction to movq symbol@GOTPCREL, targetReg.
+				var targetReg string
+				switch classifyInstruction(instructionName, argNodes) {
+				case instrPush:
+					wrappers = append(wrappers, push(d.output))
+					targetReg = "%rax"
+				case instrConditionalMove:
+					wrappers = append(wrappers, undoConditionalMove(d.output, instructionName))
+					fallthrough
+				case instrMove:
+					assertNodeType(argNodes[1], ruleRegisterOrConstant)
+					targetReg = d.contents(argNodes[1])
+				default:
+					return nil, fmt.Errorf("Cannot rewrite GOTPCREL reference for instruction %q", instructionName)
+				}
+
+				var redzoneCleared bool
+				if !isValidLEATarget(targetReg) {
+					// Sometimes the compiler will load from the GOT to an
+					// XMM register, which is not a valid target of an LEA
+					// instruction.
+					wrappers = append(wrappers, saveRegister(d.output))
+					wrappers = append(wrappers, moveTo(d.output, targetReg))
+					targetReg = "%rax"
+					redzoneCleared = true
+				}
+
+				if useGOT {
+					wrappers = append(wrappers, d.loadFromGOT(d.output, targetReg, symbol, section, redzoneCleared))
+				} else {
+					wrappers = append(wrappers, func(k func()) {
+						d.output.WriteString(fmt.Sprintf("\tleaq\t%s(%%rip), %s\n", symbol, targetReg))
+					})
+				}
+				changed = true
+				break Args
+
+			default:
+				return nil, fmt.Errorf("Unknown section type %q", section)
+			}
+
+			if !changed && len(section) > 0 {
+				panic("section was not handled")
+			}
+			section = ""
+
+			argStr := ""
+			if isIndirect {
+				argStr += "*"
+			}
+			argStr += symbol
+			argStr += offset
+
+			for ; memRef != nil; memRef = memRef.next {
+				argStr += d.contents(memRef)
+			}
+
+			args = append(args, argStr)
+
+		default:
+			panic(fmt.Sprintf("unknown instruction argument type %q", rul3s[arg.pegRule]))
+		}
+	}
+
+	if changed {
+		d.writeCommentedNode(statement)
+		replacement := "\t" + instructionName + "\t" + strings.Join(args, ", ") + "\n"
+		wrappers.do(func() {
+			d.output.WriteString(replacement)
+		})
+	} else {
+		d.writeNode(statement)
+	}
+
+	return statement, nil
+}
+
+func (d *delocation) handleBSS(statement *node32) (*node32, error) {
+	lastStatement := statement
+	for statement = statement.next; statement != nil; lastStatement, statement = statement, statement.next {
+		node := skipWS(statement.up)
+		if node == nil {
+			d.writeNode(statement)
+			continue
+		}
+
+		switch node.pegRule {
+		case ruleGlobalDirective, ruleComment, ruleInstruction, ruleLocationDirective:
+			d.writeNode(statement)
+
+		case ruleDirective:
+			directive := node.up
+			assertNodeType(directive, ruleDirectiveName)
+			directiveName := d.contents(directive)
+			if directiveName == "text" || directiveName == "section" || directiveName == "data" {
+				return lastStatement, nil
+			}
+			d.writeNode(statement)
+
+		case ruleLabel:
+			label := node.up
+			d.writeNode(statement)
+
+			if label.pegRule != ruleLocalSymbol {
+				symbol := d.contents(label)
+				localSymbol := localTargetName(symbol)
+				d.output.WriteString(fmt.Sprintf("\n%s:\n", localSymbol))
+
+				d.bssAccessorsNeeded[symbol] = localSymbol
+			}
+
+		case ruleLabelContainingDirective:
+			var err error
+			statement, err = d.processLabelContainingDirective(statement, node.up)
+			if err != nil {
+				return nil, err
+			}
+
+		default:
+			return nil, fmt.Errorf("unknown BSS statement type %q in %q", rul3s[node.pegRule], d.contents(statement))
+		}
+	}
+
+	return lastStatement, nil
+}
+
+func transform(w stringWriter, inputs []inputFile) error {
+	// symbols contains all defined symbols.
+	symbols := make(map[string]struct{})
+	// localEntrySymbols contains all symbols with a .localentry directive.
+	localEntrySymbols := make(map[string]struct{})
+
+	for _, input := range inputs {
+		forEachPath(input.ast.up, func(node *node32) {
+			symbol := input.contents[node.begin:node.end]
+			if _, ok := symbols[symbol]; ok {
+				panic(fmt.Sprintf("Duplicate symbol found: %q in %q", symbol, input.path))
+			}
+			symbols[symbol] = struct{}{}
+		}, ruleStatement, ruleLabel, ruleSymbolName)
+
+		forEachPath(input.ast.up, func(node *node32) {
+			node = node.up
+			assertNodeType(node, ruleLabelContainingDirectiveName)
+			directive := input.contents[node.begin:node.end]
+			if directive != ".localentry" {
+				return
+			}
+			// Extract the first argument.
+			node = skipWS(node.next)
+			assertNodeType(node, ruleSymbolArgs)
+			node = node.up
+			assertNodeType(node, ruleSymbolArg)
+			symbol := input.contents[node.begin:node.end]
+			if _, ok := localEntrySymbols[symbol]; ok {
+				panic(fmt.Sprintf("Duplicate .localentry directive found: %q in %q", symbol, input.path))
+			}
+			localEntrySymbols[symbol] = struct{}{}
+		}, ruleStatement, ruleLabelContainingDirective)
+	}
+
+	processor := x86_64
+	if len(inputs) > 0 {
+		processor = detectProcessor(inputs[0])
+	}
+
+	d := &delocation{
+		symbols:            symbols,
+		localEntrySymbols:  localEntrySymbols,
+		processor:          processor,
+		output:             w,
+		redirectors:        make(map[string]string),
+		bssAccessorsNeeded: make(map[string]string),
+		tocLoaders:         make(map[string]struct{}),
+		gotExternalsNeeded: make(map[string]struct{}),
+	}
+
+	w.WriteString(".text\nBORINGSSL_bcm_text_start:\n")
+
+	for _, input := range inputs {
+		if err := d.processInput(input); err != nil {
+			return err
+		}
+	}
+
+	w.WriteString(".text\nBORINGSSL_bcm_text_end:\n")
+
+	// Emit redirector functions. Each is a single jump instruction.
 	var redirectorNames []string
-	for name := range redirectors {
+	for name := range d.redirectors {
 		redirectorNames = append(redirectorNames, name)
 	}
 	sort.Strings(redirectorNames)
 
 	for _, name := range redirectorNames {
-		ret = append(ret, ".type "+name+", @function")
-		ret = append(ret, name+":")
-		ret = append(ret, "\tjmp "+redirectors[name]+"@PLT")
+		redirector := d.redirectors[name]
+		w.WriteString(".type " + redirector + ", @function\n")
+		w.WriteString(redirector + ":\n")
+		if d.processor == ppc64le {
+			w.WriteString("\tmflr 0\n")
+			w.WriteString("\tstd 0,16(1)\n")
+			w.WriteString("\tstdu 1,-32(1)\n")
+			w.WriteString("\tbl\t" + name + "\n")
+			w.WriteString("\tnop\n")
+			w.WriteString("\taddi 1,1,32\n")
+			w.WriteString("\tld 0,16(1)\n")
+			w.WriteString("\tmtlr 0\n")
+			w.WriteString("\tblr\n")
+		} else {
+			w.WriteString("\tjmp\t" + name + "\n")
+		}
 	}
 
 	var accessorNames []string
-	for accessor := range bssAccessorsNeeded {
+	for accessor := range d.bssAccessorsNeeded {
 		accessorNames = append(accessorNames, accessor)
 	}
 	sort.Strings(accessorNames)
@@ -548,129 +1189,224 @@
 	// Emit BSS accessor functions. Each is a single LEA followed by RET.
 	for _, name := range accessorNames {
 		funcName := accessorName(name)
-		ret = append(ret, ".type "+funcName+", @function")
-		ret = append(ret, funcName+":")
-		ret = append(ret, "\tleaq "+bssAccessorsNeeded[name]+"(%rip), %rax")
-		ret = append(ret, "\tret")
+		w.WriteString(".type " + funcName + ", @function\n")
+		w.WriteString(funcName + ":\n")
+		target := d.bssAccessorsNeeded[name]
+
+		if d.processor == ppc64le {
+			w.WriteString("\taddis 3, 2, " + target + "@toc@ha\n")
+			w.WriteString("\taddi 3, 3, " + target + "@toc@l\n")
+			w.WriteString("\tblr\n")
+		} else {
+			w.WriteString("\tleaq\t" + target + "(%rip), %rax\n\tret\n")
+		}
 	}
 
-	// Emit an OPENSSL_ia32cap_get accessor.
-	if ia32capGetNeeded {
-		ret = append(ret, ".type OPENSSL_ia32cap_get, @function")
-		ret = append(ret, "OPENSSL_ia32cap_get:")
-		ret = append(ret, "\tleaq OPENSSL_ia32cap_P(%rip), %rax")
-		ret = append(ret, "\tret")
+	if d.processor == ppc64le {
+		loadTOCNames := sortedSet(d.tocLoaders)
+		for _, symbol := range loadTOCNames {
+			funcName := loadTOCFuncName(symbol)
+
+			w.WriteString(".type " + funcName[2:] + ", @function\n")
+			w.WriteString(funcName[2:] + ":\n")
+			w.WriteString(funcName + ":\n")
+			w.WriteString("\taddis 3, 2, " + symbol + "@toc@ha\n")
+			w.WriteString("\taddi 3, 3, " + symbol + "@toc@l\n")
+			w.WriteString("\tblr\n")
+		}
+
+		w.WriteString(".LBORINGSSL_external_toc:\n")
+		w.WriteString(".quad .TOC.-.LBORINGSSL_external_toc\n")
+	} else {
+		externalNames := sortedSet(d.gotExternalsNeeded)
+		for _, name := range externalNames {
+			parts := strings.SplitN(name, "@", 2)
+			symbol, section := parts[0], parts[1]
+			w.WriteString(".type " + symbol + "_" + section + "_external, @object\n")
+			w.WriteString(".size " + symbol + "_" + section + "_external, 8\n")
+			w.WriteString(symbol + "_" + section + "_external:\n")
+			// Ideally this would be .quad foo@GOTPCREL, but clang's
+			// assembler cannot emit a 64-bit GOTPCREL relocation. Instead,
+			// we manually sign-extend the value, knowing that the GOT is
+			// always at the end, thus foo@GOTPCREL has a positive value.
+			w.WriteString("\t.long " + symbol + "@" + section + "\n")
+			w.WriteString("\t.long 0\n")
+		}
+
+		w.WriteString(".type OPENSSL_ia32cap_get, @function\n")
+		w.WriteString("OPENSSL_ia32cap_get:\n")
+		w.WriteString("\tleaq OPENSSL_ia32cap_P(%rip), %rax\n")
+		w.WriteString("\tret\n")
+
+		w.WriteString(".extern OPENSSL_ia32cap_P\n")
+		w.WriteString(".type OPENSSL_ia32cap_addr_delta, @object\n")
+		w.WriteString(".size OPENSSL_ia32cap_addr_delta, 8\n")
+		w.WriteString("OPENSSL_ia32cap_addr_delta:\n")
+		w.WriteString(".quad OPENSSL_ia32cap_P-OPENSSL_ia32cap_addr_delta\n")
 	}
 
-	// Emit an indirect reference to OPENSSL_ia32cap_P.
-	if ia32capAddrDeltaNeeded {
-		ret = append(ret, ".extern OPENSSL_ia32cap_P")
-		ret = append(ret, ".type OPENSSL_ia32cap_addr_delta,@object")
-		ret = append(ret, ".size OPENSSL_ia32cap_addr_delta,8")
-		ret = append(ret, "OPENSSL_ia32cap_addr_delta:")
-		ret = append(ret, "\t.quad OPENSSL_ia32cap_P-OPENSSL_ia32cap_addr_delta")
-	}
-
-	// Emit accessors for thread-local offsets.
-	var threadAccessorNames []string
-	for name := range threadLocalOffsets {
-		threadAccessorNames = append(threadAccessorNames, name)
-	}
-	sort.Strings(threadAccessorNames)
-
-	for _, name := range threadAccessorNames {
-		f := threadLocalOffsets[name]
-
-		ret = append(ret, ".type "+name+",@function")
-		ret = append(ret, name+":")
-		ret = append(ret, "\tmovq "+f.symbol+"@GOTTPOFF(%rip), %"+f.target)
-		ret = append(ret, "\tret")
-	}
-
-	// Emit external relocations for GOTPCREL offsets.
-	var gotpcrelExternalNames []string
-	for name := range gotpcrelExternalsNeeded {
-		gotpcrelExternalNames = append(gotpcrelExternalNames, name)
-	}
-	sort.Strings(gotpcrelExternalNames)
-
-	for _, name := range gotpcrelExternalNames {
-		ret = append(ret, ".type "+name+"_GOTPCREL_external, @object")
-		ret = append(ret, ".size "+name+"_GOTPCREL_external, 8")
-		ret = append(ret, name+"_GOTPCREL_external:")
-		// Ideally this would be .quad foo@GOTPCREL, but clang's
-		// assembler cannot emit a 64-bit GOTPCREL relocation. Instead,
-		// we manually sign-extend the value, knowing that the GOT is
-		// always at the end, thus foo@GOTPCREL has a positive value.
-		ret = append(ret, "\t.long "+name+"@GOTPCREL")
-		ret = append(ret, "\t.long 0")
-	}
-
-	// Emit an array for storing the module hash.
-	ret = append(ret, ".type BORINGSSL_bcm_text_hash,@object")
-	ret = append(ret, ".size BORINGSSL_bcm_text_hash,64")
-	ret = append(ret, "BORINGSSL_bcm_text_hash:")
+	w.WriteString(".type BORINGSSL_bcm_text_hash, @object\n")
+	w.WriteString(".size BORINGSSL_bcm_text_hash, 64\n")
+	w.WriteString("BORINGSSL_bcm_text_hash:\n")
 	for _, b := range uninitHashValue {
-		ret = append(ret, ".byte 0x"+strconv.FormatUint(uint64(b), 16))
+		w.WriteString(".byte 0x" + strconv.FormatUint(uint64(b), 16) + "\n")
 	}
 
-	return ret
+	return nil
 }
 
-func isLocalLabel(label string) bool {
-	if strings.HasPrefix(label, ".L") {
-		return true
-	}
-	if strings.HasSuffix(label, "f") || strings.HasSuffix(label, "b") {
-		label = label[:len(label)-1]
-		return strings.IndexFunc(label, func(r rune) bool { return !unicode.IsNumber(r) }) == -1
-	}
-	return false
-}
+func parseInputs(inputs []inputFile) error {
+	for i, input := range inputs {
+		var contents string
 
-// handleBSSSection reads lines from source until the next section and adds a
-// local symbol for each BSS symbol found.
-func handleBSSSection(lines []string, source *lineSource) (map[string]string, []string) {
-	accessors := make(map[string]string)
+		if input.isArchive {
+			arFile, err := os.Open(input.path)
+			if err != nil {
+				return err
+			}
+			defer arFile.Close()
 
-	for {
-		line, ok := source.Next()
-		if !ok {
-			return accessors, lines
+			ar, err := ParseAR(arFile)
+			if err != nil {
+				return err
+			}
+
+			if len(ar) != 1 {
+				return fmt.Errorf("expected one file in archive, but found %d", len(ar))
+			}
+
+			for _, c := range ar {
+				contents = string(c)
+			}
+		} else {
+			inBytes, err := ioutil.ReadFile(input.path)
+			if err != nil {
+				return err
+			}
+
+			contents = string(inBytes)
 		}
 
-		parts := strings.Fields(strings.TrimSpace(line))
-		if len(parts) == 0 {
-			lines = append(lines, line)
+		asm := Asm{Buffer: contents, Pretty: true}
+		asm.Init()
+		if err := asm.Parse(); err != nil {
+			return fmt.Errorf("error while parsing %q: %s", input.path, err)
+		}
+		ast := asm.AST()
+
+		inputs[i].contents = contents
+		inputs[i].ast = ast
+	}
+
+	return nil
+}
+
+func main() {
+	// The .a file, if given, is expected to be an archive of textual
+	// assembly sources. That's odd, but CMake really wants to create
+	// archive files so it's the only way that we can make it work.
+	arInput := flag.String("a", "", "Path to a .a file containing assembly sources")
+	outFile := flag.String("o", "", "Path to output assembly")
+
+	flag.Parse()
+
+	if len(*outFile) == 0 {
+		fmt.Fprintf(os.Stderr, "Must give argument to -o.\n")
+		os.Exit(1)
+	}
+
+	var inputs []inputFile
+	if len(*arInput) > 0 {
+		inputs = append(inputs, inputFile{
+			path:      *arInput,
+			index:     0,
+			isArchive: true,
+		})
+	}
+
+	for i, path := range flag.Args() {
+		if len(path) == 0 {
 			continue
 		}
 
-		if strings.HasSuffix(parts[0], ":") {
-			symbol := parts[0][:len(parts[0])-1]
-			localSymbol := ".L" + symbol + "_local_target"
+		inputs = append(inputs, inputFile{
+			path:  path,
+			index: i + 1,
+		})
+	}
 
-			lines = append(lines, line)
-			lines = append(lines, localSymbol+":")
+	if err := parseInputs(inputs); err != nil {
+		fmt.Fprintf(os.Stderr, "%s\n", err)
+		os.Exit(1)
+	}
 
-			accessors[symbol] = localSymbol
+	out, err := os.OpenFile(*outFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
+	if err != nil {
+		panic(err)
+	}
+	defer out.Close()
+
+	if err := transform(out, inputs); err != nil {
+		fmt.Fprintf(os.Stderr, "%s\n", err)
+		os.Exit(1)
+	}
+}
+
+func forEachPath(node *node32, cb func(*node32), rules ...pegRule) {
+	if node == nil {
+		return
+	}
+
+	if len(rules) == 0 {
+		cb(node)
+		return
+	}
+
+	rule := rules[0]
+	childRules := rules[1:]
+
+	for ; node != nil; node = node.next {
+		if node.pegRule != rule {
 			continue
 		}
 
-		switch parts[0] {
-		case ".text", ".section":
-			source.Unread()
-			return accessors, lines
-
-		default:
-			lines = append(lines, line)
+		if len(childRules) == 0 {
+			cb(node)
+		} else {
+			forEachPath(node.up, cb, childRules...)
 		}
 	}
 }
 
-// accessorName returns the name of the accessor function for a BSS symbol
-// named name.
-func accessorName(name string) string {
-	return name + "_bss_get"
+func skipNodes(node *node32, ruleToSkip pegRule) *node32 {
+	for ; node != nil && node.pegRule == ruleToSkip; node = node.next {
+	}
+	return node
+}
+
+func skipWS(node *node32) *node32 {
+	return skipNodes(node, ruleWS)
+}
+
+func assertNodeType(node *node32, expected pegRule) {
+	if rule := node.pegRule; rule != expected {
+		panic(fmt.Sprintf("node was %q, but wanted %q", rul3s[rule], rul3s[expected]))
+	}
+}
+
+type wrapperFunc func(func())
+
+type wrapperStack []wrapperFunc
+
+func (w *wrapperStack) do(baseCase func()) {
+	if len(*w) == 0 {
+		baseCase()
+		return
+	}
+
+	wrapper := (*w)[0]
+	*w = (*w)[1:]
+	wrapper(func() { w.do(baseCase) })
 }
 
 // localTargetName returns the name of the local target label for a global
@@ -679,6 +1415,20 @@
 	return ".L" + name + "_local_target"
 }
 
+func localEntryName(name string) string {
+	return ".L" + name + "_local_entry"
+}
+
+func isSynthesized(symbol string) bool {
+	return strings.HasSuffix(symbol, "_bss_get") ||
+		symbol == "OPENSSL_ia32cap_get" ||
+		strings.HasPrefix(symbol, "BORINGSSL_bcm_text_")
+}
+
+func redirectorName(symbol string) string {
+	return "bcm_redirector_" + symbol
+}
+
 // sectionType returns the type of a section. I.e. a section called “.text.foo”
 // is a “.text” section.
 func sectionType(section string) (string, bool) {
@@ -698,147 +1448,45 @@
 	return section, true
 }
 
-// asLines appends the contents of path to lines. Local symbols are renamed
-// using uniqueId to avoid collisions.
-func asLines(lines []string, path string, uniqueId int) ([]string, error) {
-	basename := symbolRuneOrUnderscore(filepath.Base(path))
+// accessorName returns the name of the accessor function for a BSS symbol
+// named name.
+func accessorName(name string) string {
+	return name + "_bss_get"
+}
 
-	asFile, err := os.Open(path)
-	if err != nil {
-		return nil, err
+func (d *delocation) mapLocalSymbol(symbol string) string {
+	if d.currentInput.index == 0 {
+		return symbol
 	}
-	defer asFile.Close()
+	return symbol + "_BCM_" + strconv.Itoa(d.currentInput.index)
+}
 
-	// localSymbols maps from the symbol name used in the input, to a
-	// unique symbol name.
-	localSymbols := make(map[string]string)
-
-	scanner := bufio.NewScanner(asFile)
-	var contents []string
-
-	if len(lines) == 0 {
-		// If this is the first assembly file, don't rewrite symbols.
-		// Only all-but-one file needs to be rewritten and so time can
-		// be saved by putting the (large) bcm.s first.
-		for scanner.Scan() {
-			lines = append(lines, scanner.Text())
-		}
-
-		if err := scanner.Err(); err != nil {
-			return nil, err
-		}
-
-		return lines, nil
-	}
-
-	for scanner.Scan() {
-		line := scanner.Text()
-		trimmed := strings.TrimSpace(line)
-		if strings.HasPrefix(trimmed, ".L") && strings.HasSuffix(trimmed, ":") {
-			symbol := trimmed[:len(trimmed)-1]
-			mappedSymbol := fmt.Sprintf(".L%s_%d_%s", basename, uniqueId, symbol[2:])
-			localSymbols[symbol] = mappedSymbol
-			contents = append(contents, mappedSymbol+":")
+func detectProcessor(input inputFile) processorType {
+	for statement := input.ast.up; statement != nil; statement = statement.next {
+		node := skipNodes(statement.up, ruleWS)
+		if node == nil || node.pegRule != ruleInstruction {
 			continue
 		}
 
-		contents = append(contents, line)
-	}
-	if err := scanner.Err(); err != nil {
-		return nil, err
-	}
+		instruction := node.up
+		instructionName := input.contents[instruction.begin:instruction.end]
 
-	for _, line := range contents {
-		for symbol, mappedSymbol := range localSymbols {
-			i := 0
-			for match := strings.Index(line, symbol); match >= 0; match = strings.Index(line[i:], symbol) {
-				i += match
-
-				before := ' '
-				if i > 0 {
-					before, _ = utf8.DecodeLastRuneInString(line[:i])
-				}
-
-				after, _ := utf8.DecodeRuneInString(line[i+len(symbol):])
-
-				if !symbolRune(before) && !symbolRune(after) {
-					line = strings.Replace(line, symbol, mappedSymbol, 1)
-					i += len(mappedSymbol)
-				} else {
-					i += len(symbol)
-				}
-			}
-		}
-
-		lines = append(lines, line)
-	}
-
-	return lines, nil
-}
-
-func arLines(lines []string, arPath string) ([]string, error) {
-	arFile, err := os.Open(arPath)
-	if err != nil {
-		return nil, err
-	}
-	defer arFile.Close()
-
-	ar, err := ParseAR(arFile)
-	if err != nil {
-		return nil, err
-	}
-
-	if len(ar) != 1 {
-		return nil, fmt.Errorf("expected one file in archive, but found %d", len(ar))
-	}
-
-	for _, contents := range ar {
-		scanner := bufio.NewScanner(bytes.NewBuffer(contents))
-		for scanner.Scan() {
-			lines = append(lines, scanner.Text())
-		}
-		if err := scanner.Err(); err != nil {
-			return nil, err
+		switch instructionName {
+		case "movq", "call", "leaq":
+			return x86_64
+		case "addis", "addi", "mflr":
+			return ppc64le
 		}
 	}
 
-	return lines, nil
+	panic("processed entire input and didn't recognise any instructions.")
 }
 
-// validSymbolName returns true if s is a valid (non-local) name for a symbol.
-func validSymbolName(s string) bool {
-	if len(s) == 0 {
-		return false
+func sortedSet(m map[string]struct{}) []string {
+	ret := make([]string, 0, len(m))
+	for key := range m {
+		ret = append(ret, key)
 	}
-
-	r, n := utf8.DecodeRuneInString(s)
-	// symbols don't start with a digit.
-	if r == utf8.RuneError || !symbolRune(r) || ('0' <= s[0] && s[0] <= '9') {
-		return false
-	}
-
-	return strings.IndexFunc(s[n:], func(r rune) bool {
-		return !symbolRune(r)
-	}) == -1
-}
-
-// symbolRune returns true if r is valid in a symbol name.
-func symbolRune(r rune) bool {
-	return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '$' || r == '_'
-}
-
-// symbolRuneOrUnderscore maps s where runes valid in a symbol name map to
-// themselves and all other runs map to underscore.
-func symbolRuneOrUnderscore(s string) string {
-	runes := make([]rune, 0, len(s))
-
-	for _, r := range s {
-		if symbolRune(r) {
-			runes = append(runes, r)
-		} else {
-			runes = append(runes, '_')
-		}
-	}
-
-	return string(runes)
+	sort.Strings(ret)
+	return ret
 }
diff --git a/util/fipstools/delocate.peg b/util/fipstools/delocate.peg
new file mode 100644
index 0000000..05b2e43
--- /dev/null
+++ b/util/fipstools/delocate.peg
@@ -0,0 +1,85 @@
+# Copyright (c) 2017, Google Inc.
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+# This is a rough parser for x86-64 and ppc64le assembly designed to work with
+# https://github.com/pointlander/peg. delocate.go has a go:generate line for
+# rebuilding delocate.peg.go from this file.
+
+package main
+
+type Asm Peg {}
+
+AsmFile <- Statement* !.
+Statement <- WS? (Label / ((GlobalDirective /
+                            LocationDirective /
+                            LabelContainingDirective /
+                            Instruction /
+                            Directive /
+                            Comment / ) WS? ((Comment? '\n') / ';')))
+GlobalDirective <- (".global" / ".globl") WS SymbolName
+Directive <- '.' DirectiveName (WS Args)?
+DirectiveName <- [[A-Z0-9_]]+
+LocationDirective <- (".file" / ".loc") WS [^#\n]+
+Args <- Arg ((WS? ',' WS?) Arg)*
+Arg <- QuotedArg / [[0-9a-z%+\-_@.]]*
+QuotedArg <- '"' QuotedText '"'
+QuotedText <- (EscapedChar / [^"])*
+LabelContainingDirective <- LabelContainingDirectiveName WS SymbolArgs
+LabelContainingDirectiveName <- ".long" / ".set" / ".8byte" / ".4byte" / ".quad" / ".tc" / ".localentry" / ".size" / ".type"
+SymbolArgs <- SymbolArg ((WS? ',' WS?) SymbolArg)*
+SymbolArg <- Offset /
+             SymbolType /
+             (Offset / LocalSymbol / SymbolName / Dot) WS? Operator WS? (Offset / LocalSymbol / SymbolName) /
+             LocalSymbol TCMarker? /
+             SymbolName Offset /
+             SymbolName TCMarker?
+SymbolType <- '@function' / '@object'
+Dot <- '.'
+TCMarker <- '[TC]'
+EscapedChar <- '\\' .
+WS <- [ \t]+
+Comment <- '#' [^\n]*
+Label <- (LocalSymbol / LocalLabel / SymbolName) ':'
+SymbolName <- [[A-Z._]][[A-Z.0-9$_]]*
+LocalSymbol <- '.L' [[A-Z.0-9$_]]+
+LocalLabel <- [0-9][0-9$]*
+LocalLabelRef <- [0-9][0-9$]*[bf]
+Instruction <- InstructionName (WS InstructionArg ((WS? ',' WS?) InstructionArg)*)?
+InstructionName <- [[A-Z]][[A-Z0-9]]* [.+\-]?
+InstructionArg <- IndirectionIndicator? (RegisterOrConstant / LocalLabelRef / TOCRefHigh / TOCRefLow / MemoryRef)
+TOCRefHigh <- '.TOC.-' ('0b' / ('.Lfunc_gep' [0-9]+)) "@ha"
+TOCRefLow <- '.TOC.-' ('0b' / ('.Lfunc_gep' [0-9]+)) "@l"
+IndirectionIndicator <- '*'
+RegisterOrConstant <- (('%'[[A-Z]][[A-Z0-9]]*) / ('$'? ((Offset Offset) / Offset))) ![fb:(+\-]
+# Compilers only output a very limited number of expression forms. Rather than
+# implement a full expression parser, this enumerate those forms plus a few
+# that appear in our hand-written assembly.
+MemoryRef <- (SymbolRef BaseIndexScale /
+              # Some hand-written assembly has two offsets together, e.g.
+              # “foo+16+32”. These are never GOT references.
+              SymbolRef Offset BaseIndexScale /
+              SymbolRef /
+              Offset BaseIndexScale /
+              Offset Offset BaseIndexScale /
+              Offset Offset Offset BaseIndexScale /
+              SegmentRegister Offset BaseIndexScale /
+              SegmentRegister BaseIndexScale /
+              SegmentRegister Offset /
+              BaseIndexScale)
+SymbolRef <- (Offset '+')? (LocalSymbol / SymbolName) Offset? ('@' Section)?
+BaseIndexScale <- '(' RegisterOrConstant? WS? (',' WS? RegisterOrConstant WS? (',' [0-9]+)? )? ')'
+Operator <- [+\-]
+Offset <- '+'? '-'? (("0b" [01]+) / ("0x" [[0-9A-F]]+) / [0-9]+)
+Section <- [[A-Z@]]+
+SegmentRegister <- '%' [c-gs] 's:'
diff --git a/util/fipstools/delocate.peg.go b/util/fipstools/delocate.peg.go
new file mode 100644
index 0000000..c160d46
--- /dev/null
+++ b/util/fipstools/delocate.peg.go
@@ -0,0 +1,3959 @@
+package main
+
+import (
+	"fmt"
+	"math"
+	"sort"
+	"strconv"
+)
+
+const endSymbol rune = 1114112
+
+/* The rule types inferred from the grammar are below. */
+type pegRule uint8
+
+const (
+	ruleUnknown pegRule = iota
+	ruleAsmFile
+	ruleStatement
+	ruleGlobalDirective
+	ruleDirective
+	ruleDirectiveName
+	ruleLocationDirective
+	ruleArgs
+	ruleArg
+	ruleQuotedArg
+	ruleQuotedText
+	ruleLabelContainingDirective
+	ruleLabelContainingDirectiveName
+	ruleSymbolArgs
+	ruleSymbolArg
+	ruleSymbolType
+	ruleDot
+	ruleTCMarker
+	ruleEscapedChar
+	ruleWS
+	ruleComment
+	ruleLabel
+	ruleSymbolName
+	ruleLocalSymbol
+	ruleLocalLabel
+	ruleLocalLabelRef
+	ruleInstruction
+	ruleInstructionName
+	ruleInstructionArg
+	ruleTOCRefHigh
+	ruleTOCRefLow
+	ruleIndirectionIndicator
+	ruleRegisterOrConstant
+	ruleMemoryRef
+	ruleSymbolRef
+	ruleBaseIndexScale
+	ruleOperator
+	ruleOffset
+	ruleSection
+	ruleSegmentRegister
+)
+
+var rul3s = [...]string{
+	"Unknown",
+	"AsmFile",
+	"Statement",
+	"GlobalDirective",
+	"Directive",
+	"DirectiveName",
+	"LocationDirective",
+	"Args",
+	"Arg",
+	"QuotedArg",
+	"QuotedText",
+	"LabelContainingDirective",
+	"LabelContainingDirectiveName",
+	"SymbolArgs",
+	"SymbolArg",
+	"SymbolType",
+	"Dot",
+	"TCMarker",
+	"EscapedChar",
+	"WS",
+	"Comment",
+	"Label",
+	"SymbolName",
+	"LocalSymbol",
+	"LocalLabel",
+	"LocalLabelRef",
+	"Instruction",
+	"InstructionName",
+	"InstructionArg",
+	"TOCRefHigh",
+	"TOCRefLow",
+	"IndirectionIndicator",
+	"RegisterOrConstant",
+	"MemoryRef",
+	"SymbolRef",
+	"BaseIndexScale",
+	"Operator",
+	"Offset",
+	"Section",
+	"SegmentRegister",
+}
+
+type token32 struct {
+	pegRule
+	begin, end uint32
+}
+
+func (t *token32) String() string {
+	return fmt.Sprintf("\x1B[34m%v\x1B[m %v %v", rul3s[t.pegRule], t.begin, t.end)
+}
+
+type node32 struct {
+	token32
+	up, next *node32
+}
+
+func (node *node32) print(pretty bool, buffer string) {
+	var print func(node *node32, depth int)
+	print = func(node *node32, depth int) {
+		for node != nil {
+			for c := 0; c < depth; c++ {
+				fmt.Printf(" ")
+			}
+			rule := rul3s[node.pegRule]
+			quote := strconv.Quote(string(([]rune(buffer)[node.begin:node.end])))
+			if !pretty {
+				fmt.Printf("%v %v\n", rule, quote)
+			} else {
+				fmt.Printf("\x1B[34m%v\x1B[m %v\n", rule, quote)
+			}
+			if node.up != nil {
+				print(node.up, depth+1)
+			}
+			node = node.next
+		}
+	}
+	print(node, 0)
+}
+
+func (node *node32) Print(buffer string) {
+	node.print(false, buffer)
+}
+
+func (node *node32) PrettyPrint(buffer string) {
+	node.print(true, buffer)
+}
+
+type tokens32 struct {
+	tree []token32
+}
+
+func (t *tokens32) Trim(length uint32) {
+	t.tree = t.tree[:length]
+}
+
+func (t *tokens32) Print() {
+	for _, token := range t.tree {
+		fmt.Println(token.String())
+	}
+}
+
+func (t *tokens32) AST() *node32 {
+	type element struct {
+		node *node32
+		down *element
+	}
+	tokens := t.Tokens()
+	var stack *element
+	for _, token := range tokens {
+		if token.begin == token.end {
+			continue
+		}
+		node := &node32{token32: token}
+		for stack != nil && stack.node.begin >= token.begin && stack.node.end <= token.end {
+			stack.node.next = node.up
+			node.up = stack.node
+			stack = stack.down
+		}
+		stack = &element{node: node, down: stack}
+	}
+	if stack != nil {
+		return stack.node
+	}
+	return nil
+}
+
+func (t *tokens32) PrintSyntaxTree(buffer string) {
+	t.AST().Print(buffer)
+}
+
+func (t *tokens32) PrettyPrintSyntaxTree(buffer string) {
+	t.AST().PrettyPrint(buffer)
+}
+
+func (t *tokens32) Add(rule pegRule, begin, end, index uint32) {
+	if tree := t.tree; int(index) >= len(tree) {
+		expanded := make([]token32, 2*len(tree))
+		copy(expanded, tree)
+		t.tree = expanded
+	}
+	t.tree[index] = token32{
+		pegRule: rule,
+		begin:   begin,
+		end:     end,
+	}
+}
+
+func (t *tokens32) Tokens() []token32 {
+	return t.tree
+}
+
+type Asm struct {
+	Buffer string
+	buffer []rune
+	rules  [40]func() bool
+	parse  func(rule ...int) error
+	reset  func()
+	Pretty bool
+	tokens32
+}
+
+func (p *Asm) Parse(rule ...int) error {
+	return p.parse(rule...)
+}
+
+func (p *Asm) Reset() {
+	p.reset()
+}
+
+type textPosition struct {
+	line, symbol int
+}
+
+type textPositionMap map[int]textPosition
+
+func translatePositions(buffer []rune, positions []int) textPositionMap {
+	length, translations, j, line, symbol := len(positions), make(textPositionMap, len(positions)), 0, 1, 0
+	sort.Ints(positions)
+
+search:
+	for i, c := range buffer {
+		if c == '\n' {
+			line, symbol = line+1, 0
+		} else {
+			symbol++
+		}
+		if i == positions[j] {
+			translations[positions[j]] = textPosition{line, symbol}
+			for j++; j < length; j++ {
+				if i != positions[j] {
+					continue search
+				}
+			}
+			break search
+		}
+	}
+
+	return translations
+}
+
+type parseError struct {
+	p   *Asm
+	max token32
+}
+
+func (e *parseError) Error() string {
+	tokens, error := []token32{e.max}, "\n"
+	positions, p := make([]int, 2*len(tokens)), 0
+	for _, token := range tokens {
+		positions[p], p = int(token.begin), p+1
+		positions[p], p = int(token.end), p+1
+	}
+	translations := translatePositions(e.p.buffer, positions)
+	format := "parse error near %v (line %v symbol %v - line %v symbol %v):\n%v\n"
+	if e.p.Pretty {
+		format = "parse error near \x1B[34m%v\x1B[m (line %v symbol %v - line %v symbol %v):\n%v\n"
+	}
+	for _, token := range tokens {
+		begin, end := int(token.begin), int(token.end)
+		error += fmt.Sprintf(format,
+			rul3s[token.pegRule],
+			translations[begin].line, translations[begin].symbol,
+			translations[end].line, translations[end].symbol,
+			strconv.Quote(string(e.p.buffer[begin:end])))
+	}
+
+	return error
+}
+
+func (p *Asm) PrintSyntaxTree() {
+	if p.Pretty {
+		p.tokens32.PrettyPrintSyntaxTree(p.Buffer)
+	} else {
+		p.tokens32.PrintSyntaxTree(p.Buffer)
+	}
+}
+
+func (p *Asm) Init() {
+	var (
+		max                  token32
+		position, tokenIndex uint32
+		buffer               []rune
+	)
+	p.reset = func() {
+		max = token32{}
+		position, tokenIndex = 0, 0
+
+		p.buffer = []rune(p.Buffer)
+		if len(p.buffer) == 0 || p.buffer[len(p.buffer)-1] != endSymbol {
+			p.buffer = append(p.buffer, endSymbol)
+		}
+		buffer = p.buffer
+	}
+	p.reset()
+
+	_rules := p.rules
+	tree := tokens32{tree: make([]token32, math.MaxInt16)}
+	p.parse = func(rule ...int) error {
+		r := 1
+		if len(rule) > 0 {
+			r = rule[0]
+		}
+		matches := p.rules[r]()
+		p.tokens32 = tree
+		if matches {
+			p.Trim(tokenIndex)
+			return nil
+		}
+		return &parseError{p, max}
+	}
+
+	add := func(rule pegRule, begin uint32) {
+		tree.Add(rule, begin, position, tokenIndex)
+		tokenIndex++
+		if begin != position && position > max.end {
+			max = token32{rule, begin, position}
+		}
+	}
+
+	matchDot := func() bool {
+		if buffer[position] != endSymbol {
+			position++
+			return true
+		}
+		return false
+	}
+
+	/*matchChar := func(c byte) bool {
+		if buffer[position] == c {
+			position++
+			return true
+		}
+		return false
+	}*/
+
+	/*matchRange := func(lower byte, upper byte) bool {
+		if c := buffer[position]; c >= lower && c <= upper {
+			position++
+			return true
+		}
+		return false
+	}*/
+
+	_rules = [...]func() bool{
+		nil,
+		/* 0 AsmFile <- <(Statement* !.)> */
+		func() bool {
+			position0, tokenIndex0 := position, tokenIndex
+			{
+				position1 := position
+			l2:
+				{
+					position3, tokenIndex3 := position, tokenIndex
+					if !_rules[ruleStatement]() {
+						goto l3
+					}
+					goto l2
+				l3:
+					position, tokenIndex = position3, tokenIndex3
+				}
+				{
+					position4, tokenIndex4 := position, tokenIndex
+					if !matchDot() {
+						goto l4
+					}
+					goto l0
+				l4:
+					position, tokenIndex = position4, tokenIndex4
+				}
+				add(ruleAsmFile, position1)
+			}
+			return true
+		l0:
+			position, tokenIndex = position0, tokenIndex0
+			return false
+		},
+		/* 1 Statement <- <(WS? (Label / ((GlobalDirective / LocationDirective / LabelContainingDirective / Instruction / Directive / Comment / ) WS? ((Comment? '\n') / ';'))))> */
+		func() bool {
+			position5, tokenIndex5 := position, tokenIndex
+			{
+				position6 := position
+				{
+					position7, tokenIndex7 := position, tokenIndex
+					if !_rules[ruleWS]() {
+						goto l7
+					}
+					goto l8
+				l7:
+					position, tokenIndex = position7, tokenIndex7
+				}
+			l8:
+				{
+					position9, tokenIndex9 := position, tokenIndex
+					if !_rules[ruleLabel]() {
+						goto l10
+					}
+					goto l9
+				l10:
+					position, tokenIndex = position9, tokenIndex9
+					{
+						position11, tokenIndex11 := position, tokenIndex
+						if !_rules[ruleGlobalDirective]() {
+							goto l12
+						}
+						goto l11
+					l12:
+						position, tokenIndex = position11, tokenIndex11
+						if !_rules[ruleLocationDirective]() {
+							goto l13
+						}
+						goto l11
+					l13:
+						position, tokenIndex = position11, tokenIndex11
+						if !_rules[ruleLabelContainingDirective]() {
+							goto l14
+						}
+						goto l11
+					l14:
+						position, tokenIndex = position11, tokenIndex11
+						if !_rules[ruleInstruction]() {
+							goto l15
+						}
+						goto l11
+					l15:
+						position, tokenIndex = position11, tokenIndex11
+						if !_rules[ruleDirective]() {
+							goto l16
+						}
+						goto l11
+					l16:
+						position, tokenIndex = position11, tokenIndex11
+						if !_rules[ruleComment]() {
+							goto l17
+						}
+						goto l11
+					l17:
+						position, tokenIndex = position11, tokenIndex11
+					}
+				l11:
+					{
+						position18, tokenIndex18 := position, tokenIndex
+						if !_rules[ruleWS]() {
+							goto l18
+						}
+						goto l19
+					l18:
+						position, tokenIndex = position18, tokenIndex18
+					}
+				l19:
+					{
+						position20, tokenIndex20 := position, tokenIndex
+						{
+							position22, tokenIndex22 := position, tokenIndex
+							if !_rules[ruleComment]() {
+								goto l22
+							}
+							goto l23
+						l22:
+							position, tokenIndex = position22, tokenIndex22
+						}
+					l23:
+						if buffer[position] != rune('\n') {
+							goto l21
+						}
+						position++
+						goto l20
+					l21:
+						position, tokenIndex = position20, tokenIndex20
+						if buffer[position] != rune(';') {
+							goto l5
+						}
+						position++
+					}
+				l20:
+				}
+			l9:
+				add(ruleStatement, position6)
+			}
+			return true
+		l5:
+			position, tokenIndex = position5, tokenIndex5
+			return false
+		},
+		/* 2 GlobalDirective <- <((('.' ('g' / 'G') ('l' / 'L') ('o' / 'O') ('b' / 'B') ('a' / 'A') ('l' / 'L')) / ('.' ('g' / 'G') ('l' / 'L') ('o' / 'O') ('b' / 'B') ('l' / 'L'))) WS SymbolName)> */
+		func() bool {
+			position24, tokenIndex24 := position, tokenIndex
+			{
+				position25 := position
+				{
+					position26, tokenIndex26 := position, tokenIndex
+					if buffer[position] != rune('.') {
+						goto l27
+					}
+					position++
+					{
+						position28, tokenIndex28 := position, tokenIndex
+						if buffer[position] != rune('g') {
+							goto l29
+						}
+						position++
+						goto l28
+					l29:
+						position, tokenIndex = position28, tokenIndex28
+						if buffer[position] != rune('G') {
+							goto l27
+						}
+						position++
+					}
+				l28:
+					{
+						position30, tokenIndex30 := position, tokenIndex
+						if buffer[position] != rune('l') {
+							goto l31
+						}
+						position++
+						goto l30
+					l31:
+						position, tokenIndex = position30, tokenIndex30
+						if buffer[position] != rune('L') {
+							goto l27
+						}
+						position++
+					}
+				l30:
+					{
+						position32, tokenIndex32 := position, tokenIndex
+						if buffer[position] != rune('o') {
+							goto l33
+						}
+						position++
+						goto l32
+					l33:
+						position, tokenIndex = position32, tokenIndex32
+						if buffer[position] != rune('O') {
+							goto l27
+						}
+						position++
+					}
+				l32:
+					{
+						position34, tokenIndex34 := position, tokenIndex
+						if buffer[position] != rune('b') {
+							goto l35
+						}
+						position++
+						goto l34
+					l35:
+						position, tokenIndex = position34, tokenIndex34
+						if buffer[position] != rune('B') {
+							goto l27
+						}
+						position++
+					}
+				l34:
+					{
+						position36, tokenIndex36 := position, tokenIndex
+						if buffer[position] != rune('a') {
+							goto l37
+						}
+						position++
+						goto l36
+					l37:
+						position, tokenIndex = position36, tokenIndex36
+						if buffer[position] != rune('A') {
+							goto l27
+						}
+						position++
+					}
+				l36:
+					{
+						position38, tokenIndex38 := position, tokenIndex
+						if buffer[position] != rune('l') {
+							goto l39
+						}
+						position++
+						goto l38
+					l39:
+						position, tokenIndex = position38, tokenIndex38
+						if buffer[position] != rune('L') {
+							goto l27
+						}
+						position++
+					}
+				l38:
+					goto l26
+				l27:
+					position, tokenIndex = position26, tokenIndex26
+					if buffer[position] != rune('.') {
+						goto l24
+					}
+					position++
+					{
+						position40, tokenIndex40 := position, tokenIndex
+						if buffer[position] != rune('g') {
+							goto l41
+						}
+						position++
+						goto l40
+					l41:
+						position, tokenIndex = position40, tokenIndex40
+						if buffer[position] != rune('G') {
+							goto l24
+						}
+						position++
+					}
+				l40:
+					{
+						position42, tokenIndex42 := position, tokenIndex
+						if buffer[position] != rune('l') {
+							goto l43
+						}
+						position++
+						goto l42
+					l43:
+						position, tokenIndex = position42, tokenIndex42
+						if buffer[position] != rune('L') {
+							goto l24
+						}
+						position++
+					}
+				l42:
+					{
+						position44, tokenIndex44 := position, tokenIndex
+						if buffer[position] != rune('o') {
+							goto l45
+						}
+						position++
+						goto l44
+					l45:
+						position, tokenIndex = position44, tokenIndex44
+						if buffer[position] != rune('O') {
+							goto l24
+						}
+						position++
+					}
+				l44:
+					{
+						position46, tokenIndex46 := position, tokenIndex
+						if buffer[position] != rune('b') {
+							goto l47
+						}
+						position++
+						goto l46
+					l47:
+						position, tokenIndex = position46, tokenIndex46
+						if buffer[position] != rune('B') {
+							goto l24
+						}
+						position++
+					}
+				l46:
+					{
+						position48, tokenIndex48 := position, tokenIndex
+						if buffer[position] != rune('l') {
+							goto l49
+						}
+						position++
+						goto l48
+					l49:
+						position, tokenIndex = position48, tokenIndex48
+						if buffer[position] != rune('L') {
+							goto l24
+						}
+						position++
+					}
+				l48:
+				}
+			l26:
+				if !_rules[ruleWS]() {
+					goto l24
+				}
+				if !_rules[ruleSymbolName]() {
+					goto l24
+				}
+				add(ruleGlobalDirective, position25)
+			}
+			return true
+		l24:
+			position, tokenIndex = position24, tokenIndex24
+			return false
+		},
+		/* 3 Directive <- <('.' DirectiveName (WS Args)?)> */
+		func() bool {
+			position50, tokenIndex50 := position, tokenIndex
+			{
+				position51 := position
+				if buffer[position] != rune('.') {
+					goto l50
+				}
+				position++
+				if !_rules[ruleDirectiveName]() {
+					goto l50
+				}
+				{
+					position52, tokenIndex52 := position, tokenIndex
+					if !_rules[ruleWS]() {
+						goto l52
+					}
+					if !_rules[ruleArgs]() {
+						goto l52
+					}
+					goto l53
+				l52:
+					position, tokenIndex = position52, tokenIndex52
+				}
+			l53:
+				add(ruleDirective, position51)
+			}
+			return true
+		l50:
+			position, tokenIndex = position50, tokenIndex50
+			return false
+		},
+		/* 4 DirectiveName <- <([a-z] / [A-Z] / ([0-9] / [0-9]) / '_')+> */
+		func() bool {
+			position54, tokenIndex54 := position, tokenIndex
+			{
+				position55 := position
+				{
+					position58, tokenIndex58 := position, tokenIndex
+					if c := buffer[position]; c < rune('a') || c > rune('z') {
+						goto l59
+					}
+					position++
+					goto l58
+				l59:
+					position, tokenIndex = position58, tokenIndex58
+					if c := buffer[position]; c < rune('A') || c > rune('Z') {
+						goto l60
+					}
+					position++
+					goto l58
+				l60:
+					position, tokenIndex = position58, tokenIndex58
+					{
+						position62, tokenIndex62 := position, tokenIndex
+						if c := buffer[position]; c < rune('0') || c > rune('9') {
+							goto l63
+						}
+						position++
+						goto l62
+					l63:
+						position, tokenIndex = position62, tokenIndex62
+						if c := buffer[position]; c < rune('0') || c > rune('9') {
+							goto l61
+						}
+						position++
+					}
+				l62:
+					goto l58
+				l61:
+					position, tokenIndex = position58, tokenIndex58
+					if buffer[position] != rune('_') {
+						goto l54
+					}
+					position++
+				}
+			l58:
+			l56:
+				{
+					position57, tokenIndex57 := position, tokenIndex
+					{
+						position64, tokenIndex64 := position, tokenIndex
+						if c := buffer[position]; c < rune('a') || c > rune('z') {
+							goto l65
+						}
+						position++
+						goto l64
+					l65:
+						position, tokenIndex = position64, tokenIndex64
+						if c := buffer[position]; c < rune('A') || c > rune('Z') {
+							goto l66
+						}
+						position++
+						goto l64
+					l66:
+						position, tokenIndex = position64, tokenIndex64
+						{
+							position68, tokenIndex68 := position, tokenIndex
+							if c := buffer[position]; c < rune('0') || c > rune('9') {
+								goto l69
+							}
+							position++
+							goto l68
+						l69:
+							position, tokenIndex = position68, tokenIndex68
+							if c := buffer[position]; c < rune('0') || c > rune('9') {
+								goto l67
+							}
+							position++
+						}
+					l68:
+						goto l64
+					l67:
+						position, tokenIndex = position64, tokenIndex64
+						if buffer[position] != rune('_') {
+							goto l57
+						}
+						position++
+					}
+				l64:
+					goto l56
+				l57:
+					position, tokenIndex = position57, tokenIndex57
+				}
+				add(ruleDirectiveName, position55)
+			}
+			return true
+		l54:
+			position, tokenIndex = position54, tokenIndex54
+			return false
+		},
+		/* 5 LocationDirective <- <((('.' ('f' / 'F') ('i' / 'I') ('l' / 'L') ('e' / 'E')) / ('.' ('l' / 'L') ('o' / 'O') ('c' / 'C'))) WS (!('#' / '\n') .)+)> */
+		func() bool {
+			position70, tokenIndex70 := position, tokenIndex
+			{
+				position71 := position
+				{
+					position72, tokenIndex72 := position, tokenIndex
+					if buffer[position] != rune('.') {
+						goto l73
+					}
+					position++
+					{
+						position74, tokenIndex74 := position, tokenIndex
+						if buffer[position] != rune('f') {
+							goto l75
+						}
+						position++
+						goto l74
+					l75:
+						position, tokenIndex = position74, tokenIndex74
+						if buffer[position] != rune('F') {
+							goto l73
+						}
+						position++
+					}
+				l74:
+					{
+						position76, tokenIndex76 := position, tokenIndex
+						if buffer[position] != rune('i') {
+							goto l77
+						}
+						position++
+						goto l76
+					l77:
+						position, tokenIndex = position76, tokenIndex76
+						if buffer[position] != rune('I') {
+							goto l73
+						}
+						position++
+					}
+				l76:
+					{
+						position78, tokenIndex78 := position, tokenIndex
+						if buffer[position] != rune('l') {
+							goto l79
+						}
+						position++
+						goto l78
+					l79:
+						position, tokenIndex = position78, tokenIndex78
+						if buffer[position] != rune('L') {
+							goto l73
+						}
+						position++
+					}
+				l78:
+					{
+						position80, tokenIndex80 := position, tokenIndex
+						if buffer[position] != rune('e') {
+							goto l81
+						}
+						position++
+						goto l80
+					l81:
+						position, tokenIndex = position80, tokenIndex80
+						if buffer[position] != rune('E') {
+							goto l73
+						}
+						position++
+					}
+				l80:
+					goto l72
+				l73:
+					position, tokenIndex = position72, tokenIndex72
+					if buffer[position] != rune('.') {
+						goto l70
+					}
+					position++
+					{
+						position82, tokenIndex82 := position, tokenIndex
+						if buffer[position] != rune('l') {
+							goto l83
+						}
+						position++
+						goto l82
+					l83:
+						position, tokenIndex = position82, tokenIndex82
+						if buffer[position] != rune('L') {
+							goto l70
+						}
+						position++
+					}
+				l82:
+					{
+						position84, tokenIndex84 := position, tokenIndex
+						if buffer[position] != rune('o') {
+							goto l85
+						}
+						position++
+						goto l84
+					l85:
+						position, tokenIndex = position84, tokenIndex84
+						if buffer[position] != rune('O') {
+							goto l70
+						}
+						position++
+					}
+				l84:
+					{
+						position86, tokenIndex86 := position, tokenIndex
+						if buffer[position] != rune('c') {
+							goto l87
+						}
+						position++
+						goto l86
+					l87:
+						position, tokenIndex = position86, tokenIndex86
+						if buffer[position] != rune('C') {
+							goto l70
+						}
+						position++
+					}
+				l86:
+				}
+			l72:
+				if !_rules[ruleWS]() {
+					goto l70
+				}
+				{
+					position90, tokenIndex90 := position, tokenIndex
+					{
+						position91, tokenIndex91 := position, tokenIndex
+						if buffer[position] != rune('#') {
+							goto l92
+						}
+						position++
+						goto l91
+					l92:
+						position, tokenIndex = position91, tokenIndex91
+						if buffer[position] != rune('\n') {
+							goto l90
+						}
+						position++
+					}
+				l91:
+					goto l70
+				l90:
+					position, tokenIndex = position90, tokenIndex90
+				}
+				if !matchDot() {
+					goto l70
+				}
+			l88:
+				{
+					position89, tokenIndex89 := position, tokenIndex
+					{
+						position93, tokenIndex93 := position, tokenIndex
+						{
+							position94, tokenIndex94 := position, tokenIndex
+							if buffer[position] != rune('#') {
+								goto l95
+							}
+							position++
+							goto l94
+						l95:
+							position, tokenIndex = position94, tokenIndex94
+							if buffer[position] != rune('\n') {
+								goto l93
+							}
+							position++
+						}
+					l94:
+						goto l89
+					l93:
+						position, tokenIndex = position93, tokenIndex93
+					}
+					if !matchDot() {
+						goto l89
+					}
+					goto l88
+				l89:
+					position, tokenIndex = position89, tokenIndex89
+				}
+				add(ruleLocationDirective, position71)
+			}
+			return true
+		l70:
+			position, tokenIndex = position70, tokenIndex70
+			return false
+		},
+		/* 6 Args <- <(Arg (WS? ',' WS? Arg)*)> */
+		func() bool {
+			position96, tokenIndex96 := position, tokenIndex
+			{
+				position97 := position
+				if !_rules[ruleArg]() {
+					goto l96
+				}
+			l98:
+				{
+					position99, tokenIndex99 := position, tokenIndex
+					{
+						position100, tokenIndex100 := position, tokenIndex
+						if !_rules[ruleWS]() {
+							goto l100
+						}
+						goto l101
+					l100:
+						position, tokenIndex = position100, tokenIndex100
+					}
+				l101:
+					if buffer[position] != rune(',') {
+						goto l99
+					}
+					position++
+					{
+						position102, tokenIndex102 := position, tokenIndex
+						if !_rules[ruleWS]() {
+							goto l102
+						}
+						goto l103
+					l102:
+						position, tokenIndex = position102, tokenIndex102
+					}
+				l103:
+					if !_rules[ruleArg]() {
+						goto l99
+					}
+					goto l98
+				l99:
+					position, tokenIndex = position99, tokenIndex99
+				}
+				add(ruleArgs, position97)
+			}
+			return true
+		l96:
+			position, tokenIndex = position96, tokenIndex96
+			return false
+		},
+		/* 7 Arg <- <(QuotedArg / ([0-9] / [0-9] / ([a-z] / [A-Z]) / '%' / '+' / '-' / '_' / '@' / '.')*)> */
+		func() bool {
+			{
+				position105 := position
+				{
+					position106, tokenIndex106 := position, tokenIndex
+					if !_rules[ruleQuotedArg]() {
+						goto l107
+					}
+					goto l106
+				l107:
+					position, tokenIndex = position106, tokenIndex106
+				l108:
+					{
+						position109, tokenIndex109 := position, tokenIndex
+						{
+							position110, tokenIndex110 := position, tokenIndex
+							if c := buffer[position]; c < rune('0') || c > rune('9') {
+								goto l111
+							}
+							position++
+							goto l110
+						l111:
+							position, tokenIndex = position110, tokenIndex110
+							if c := buffer[position]; c < rune('0') || c > rune('9') {
+								goto l112
+							}
+							position++
+							goto l110
+						l112:
+							position, tokenIndex = position110, tokenIndex110
+							{
+								position114, tokenIndex114 := position, tokenIndex
+								if c := buffer[position]; c < rune('a') || c > rune('z') {
+									goto l115
+								}
+								position++
+								goto l114
+							l115:
+								position, tokenIndex = position114, tokenIndex114
+								if c := buffer[position]; c < rune('A') || c > rune('Z') {
+									goto l113
+								}
+								position++
+							}
+						l114:
+							goto l110
+						l113:
+							position, tokenIndex = position110, tokenIndex110
+							if buffer[position] != rune('%') {
+								goto l116
+							}
+							position++
+							goto l110
+						l116:
+							position, tokenIndex = position110, tokenIndex110
+							if buffer[position] != rune('+') {
+								goto l117
+							}
+							position++
+							goto l110
+						l117:
+							position, tokenIndex = position110, tokenIndex110
+							if buffer[position] != rune('-') {
+								goto l118
+							}
+							position++
+							goto l110
+						l118:
+							position, tokenIndex = position110, tokenIndex110
+							if buffer[position] != rune('_') {
+								goto l119
+							}
+							position++
+							goto l110
+						l119:
+							position, tokenIndex = position110, tokenIndex110
+							if buffer[position] != rune('@') {
+								goto l120
+							}
+							position++
+							goto l110
+						l120:
+							position, tokenIndex = position110, tokenIndex110
+							if buffer[position] != rune('.') {
+								goto l109
+							}
+							position++
+						}
+					l110:
+						goto l108
+					l109:
+						position, tokenIndex = position109, tokenIndex109
+					}
+				}
+			l106:
+				add(ruleArg, position105)
+			}
+			return true
+		},
+		/* 8 QuotedArg <- <('"' QuotedText '"')> */
+		func() bool {
+			position121, tokenIndex121 := position, tokenIndex
+			{
+				position122 := position
+				if buffer[position] != rune('"') {
+					goto l121
+				}
+				position++
+				if !_rules[ruleQuotedText]() {
+					goto l121
+				}
+				if buffer[position] != rune('"') {
+					goto l121
+				}
+				position++
+				add(ruleQuotedArg, position122)
+			}
+			return true
+		l121:
+			position, tokenIndex = position121, tokenIndex121
+			return false
+		},
+		/* 9 QuotedText <- <(EscapedChar / (!'"' .))*> */
+		func() bool {
+			{
+				position124 := position
+			l125:
+				{
+					position126, tokenIndex126 := position, tokenIndex
+					{
+						position127, tokenIndex127 := position, tokenIndex
+						if !_rules[ruleEscapedChar]() {
+							goto l128
+						}
+						goto l127
+					l128:
+						position, tokenIndex = position127, tokenIndex127
+						{
+							position129, tokenIndex129 := position, tokenIndex
+							if buffer[position] != rune('"') {
+								goto l129
+							}
+							position++
+							goto l126
+						l129:
+							position, tokenIndex = position129, tokenIndex129
+						}
+						if !matchDot() {
+							goto l126
+						}
+					}
+				l127:
+					goto l125
+				l126:
+					position, tokenIndex = position126, tokenIndex126
+				}
+				add(ruleQuotedText, position124)
+			}
+			return true
+		},
+		/* 10 LabelContainingDirective <- <(LabelContainingDirectiveName WS SymbolArgs)> */
+		func() bool {
+			position130, tokenIndex130 := position, tokenIndex
+			{
+				position131 := position
+				if !_rules[ruleLabelContainingDirectiveName]() {
+					goto l130
+				}
+				if !_rules[ruleWS]() {
+					goto l130
+				}
+				if !_rules[ruleSymbolArgs]() {
+					goto l130
+				}
+				add(ruleLabelContainingDirective, position131)
+			}
+			return true
+		l130:
+			position, tokenIndex = position130, tokenIndex130
+			return false
+		},
+		/* 11 LabelContainingDirectiveName <- <(('.' ('l' / 'L') ('o' / 'O') ('n' / 'N') ('g' / 'G')) / ('.' ('s' / 'S') ('e' / 'E') ('t' / 'T')) / ('.' '8' ('b' / 'B') ('y' / 'Y') ('t' / 'T') ('e' / 'E')) / ('.' '4' ('b' / 'B') ('y' / 'Y') ('t' / 'T') ('e' / 'E')) / ('.' ('q' / 'Q') ('u' / 'U') ('a' / 'A') ('d' / 'D')) / ('.' ('t' / 'T') ('c' / 'C')) / ('.' ('l' / 'L') ('o' / 'O') ('c' / 'C') ('a' / 'A') ('l' / 'L') ('e' / 'E') ('n' / 'N') ('t' / 'T') ('r' / 'R') ('y' / 'Y')) / ('.' ('s' / 'S') ('i' / 'I') ('z' / 'Z') ('e' / 'E')) / ('.' ('t' / 'T') ('y' / 'Y') ('p' / 'P') ('e' / 'E')))> */
+		func() bool {
+			position132, tokenIndex132 := position, tokenIndex
+			{
+				position133 := position
+				{
+					position134, tokenIndex134 := position, tokenIndex
+					if buffer[position] != rune('.') {
+						goto l135
+					}
+					position++
+					{
+						position136, tokenIndex136 := position, tokenIndex
+						if buffer[position] != rune('l') {
+							goto l137
+						}
+						position++
+						goto l136
+					l137:
+						position, tokenIndex = position136, tokenIndex136
+						if buffer[position] != rune('L') {
+							goto l135
+						}
+						position++
+					}
+				l136:
+					{
+						position138, tokenIndex138 := position, tokenIndex
+						if buffer[position] != rune('o') {
+							goto l139
+						}
+						position++
+						goto l138
+					l139:
+						position, tokenIndex = position138, tokenIndex138
+						if buffer[position] != rune('O') {
+							goto l135
+						}
+						position++
+					}
+				l138:
+					{
+						position140, tokenIndex140 := position, tokenIndex
+						if buffer[position] != rune('n') {
+							goto l141
+						}
+						position++
+						goto l140
+					l141:
+						position, tokenIndex = position140, tokenIndex140
+						if buffer[position] != rune('N') {
+							goto l135
+						}
+						position++
+					}
+				l140:
+					{
+						position142, tokenIndex142 := position, tokenIndex
+						if buffer[position] != rune('g') {
+							goto l143
+						}
+						position++
+						goto l142
+					l143:
+						position, tokenIndex = position142, tokenIndex142
+						if buffer[position] != rune('G') {
+							goto l135
+						}
+						position++
+					}
+				l142:
+					goto l134
+				l135:
+					position, tokenIndex = position134, tokenIndex134
+					if buffer[position] != rune('.') {
+						goto l144
+					}
+					position++
+					{
+						position145, tokenIndex145 := position, tokenIndex
+						if buffer[position] != rune('s') {
+							goto l146
+						}
+						position++
+						goto l145
+					l146:
+						position, tokenIndex = position145, tokenIndex145
+						if buffer[position] != rune('S') {
+							goto l144
+						}
+						position++
+					}
+				l145:
+					{
+						position147, tokenIndex147 := position, tokenIndex
+						if buffer[position] != rune('e') {
+							goto l148
+						}
+						position++
+						goto l147
+					l148:
+						position, tokenIndex = position147, tokenIndex147
+						if buffer[position] != rune('E') {
+							goto l144
+						}
+						position++
+					}
+				l147:
+					{
+						position149, tokenIndex149 := position, tokenIndex
+						if buffer[position] != rune('t') {
+							goto l150
+						}
+						position++
+						goto l149
+					l150:
+						position, tokenIndex = position149, tokenIndex149
+						if buffer[position] != rune('T') {
+							goto l144
+						}
+						position++
+					}
+				l149:
+					goto l134
+				l144:
+					position, tokenIndex = position134, tokenIndex134
+					if buffer[position] != rune('.') {
+						goto l151
+					}
+					position++
+					if buffer[position] != rune('8') {
+						goto l151
+					}
+					position++
+					{
+						position152, tokenIndex152 := position, tokenIndex
+						if buffer[position] != rune('b') {
+							goto l153
+						}
+						position++
+						goto l152
+					l153:
+						position, tokenIndex = position152, tokenIndex152
+						if buffer[position] != rune('B') {
+							goto l151
+						}
+						position++
+					}
+				l152:
+					{
+						position154, tokenIndex154 := position, tokenIndex
+						if buffer[position] != rune('y') {
+							goto l155
+						}
+						position++
+						goto l154
+					l155:
+						position, tokenIndex = position154, tokenIndex154
+						if buffer[position] != rune('Y') {
+							goto l151
+						}
+						position++
+					}
+				l154:
+					{
+						position156, tokenIndex156 := position, tokenIndex
+						if buffer[position] != rune('t') {
+							goto l157
+						}
+						position++
+						goto l156
+					l157:
+						position, tokenIndex = position156, tokenIndex156
+						if buffer[position] != rune('T') {
+							goto l151
+						}
+						position++
+					}
+				l156:
+					{
+						position158, tokenIndex158 := position, tokenIndex
+						if buffer[position] != rune('e') {
+							goto l159
+						}
+						position++
+						goto l158
+					l159:
+						position, tokenIndex = position158, tokenIndex158
+						if buffer[position] != rune('E') {
+							goto l151
+						}
+						position++
+					}
+				l158:
+					goto l134
+				l151:
+					position, tokenIndex = position134, tokenIndex134
+					if buffer[position] != rune('.') {
+						goto l160
+					}
+					position++
+					if buffer[position] != rune('4') {
+						goto l160
+					}
+					position++
+					{
+						position161, tokenIndex161 := position, tokenIndex
+						if buffer[position] != rune('b') {
+							goto l162
+						}
+						position++
+						goto l161
+					l162:
+						position, tokenIndex = position161, tokenIndex161
+						if buffer[position] != rune('B') {
+							goto l160
+						}
+						position++
+					}
+				l161:
+					{
+						position163, tokenIndex163 := position, tokenIndex
+						if buffer[position] != rune('y') {
+							goto l164
+						}
+						position++
+						goto l163
+					l164:
+						position, tokenIndex = position163, tokenIndex163
+						if buffer[position] != rune('Y') {
+							goto l160
+						}
+						position++
+					}
+				l163:
+					{
+						position165, tokenIndex165 := position, tokenIndex
+						if buffer[position] != rune('t') {
+							goto l166
+						}
+						position++
+						goto l165
+					l166:
+						position, tokenIndex = position165, tokenIndex165
+						if buffer[position] != rune('T') {
+							goto l160
+						}
+						position++
+					}
+				l165:
+					{
+						position167, tokenIndex167 := position, tokenIndex
+						if buffer[position] != rune('e') {
+							goto l168
+						}
+						position++
+						goto l167
+					l168:
+						position, tokenIndex = position167, tokenIndex167
+						if buffer[position] != rune('E') {
+							goto l160
+						}
+						position++
+					}
+				l167:
+					goto l134
+				l160:
+					position, tokenIndex = position134, tokenIndex134
+					if buffer[position] != rune('.') {
+						goto l169
+					}
+					position++
+					{
+						position170, tokenIndex170 := position, tokenIndex
+						if buffer[position] != rune('q') {
+							goto l171
+						}
+						position++
+						goto l170
+					l171:
+						position, tokenIndex = position170, tokenIndex170
+						if buffer[position] != rune('Q') {
+							goto l169
+						}
+						position++
+					}
+				l170:
+					{
+						position172, tokenIndex172 := position, tokenIndex
+						if buffer[position] != rune('u') {
+							goto l173
+						}
+						position++
+						goto l172
+					l173:
+						position, tokenIndex = position172, tokenIndex172
+						if buffer[position] != rune('U') {
+							goto l169
+						}
+						position++
+					}
+				l172:
+					{
+						position174, tokenIndex174 := position, tokenIndex
+						if buffer[position] != rune('a') {
+							goto l175
+						}
+						position++
+						goto l174
+					l175:
+						position, tokenIndex = position174, tokenIndex174
+						if buffer[position] != rune('A') {
+							goto l169
+						}
+						position++
+					}
+				l174:
+					{
+						position176, tokenIndex176 := position, tokenIndex
+						if buffer[position] != rune('d') {
+							goto l177
+						}
+						position++
+						goto l176
+					l177:
+						position, tokenIndex = position176, tokenIndex176
+						if buffer[position] != rune('D') {
+							goto l169
+						}
+						position++
+					}
+				l176:
+					goto l134
+				l169:
+					position, tokenIndex = position134, tokenIndex134
+					if buffer[position] != rune('.') {
+						goto l178
+					}
+					position++
+					{
+						position179, tokenIndex179 := position, tokenIndex
+						if buffer[position] != rune('t') {
+							goto l180
+						}
+						position++
+						goto l179
+					l180:
+						position, tokenIndex = position179, tokenIndex179
+						if buffer[position] != rune('T') {
+							goto l178
+						}
+						position++
+					}
+				l179:
+					{
+						position181, tokenIndex181 := position, tokenIndex
+						if buffer[position] != rune('c') {
+							goto l182
+						}
+						position++
+						goto l181
+					l182:
+						position, tokenIndex = position181, tokenIndex181
+						if buffer[position] != rune('C') {
+							goto l178
+						}
+						position++
+					}
+				l181:
+					goto l134
+				l178:
+					position, tokenIndex = position134, tokenIndex134
+					if buffer[position] != rune('.') {
+						goto l183
+					}
+					position++
+					{
+						position184, tokenIndex184 := position, tokenIndex
+						if buffer[position] != rune('l') {
+							goto l185
+						}
+						position++
+						goto l184
+					l185:
+						position, tokenIndex = position184, tokenIndex184
+						if buffer[position] != rune('L') {
+							goto l183
+						}
+						position++
+					}
+				l184:
+					{
+						position186, tokenIndex186 := position, tokenIndex
+						if buffer[position] != rune('o') {
+							goto l187
+						}
+						position++
+						goto l186
+					l187:
+						position, tokenIndex = position186, tokenIndex186
+						if buffer[position] != rune('O') {
+							goto l183
+						}
+						position++
+					}
+				l186:
+					{
+						position188, tokenIndex188 := position, tokenIndex
+						if buffer[position] != rune('c') {
+							goto l189
+						}
+						position++
+						goto l188
+					l189:
+						position, tokenIndex = position188, tokenIndex188
+						if buffer[position] != rune('C') {
+							goto l183
+						}
+						position++
+					}
+				l188:
+					{
+						position190, tokenIndex190 := position, tokenIndex
+						if buffer[position] != rune('a') {
+							goto l191
+						}
+						position++
+						goto l190
+					l191:
+						position, tokenIndex = position190, tokenIndex190
+						if buffer[position] != rune('A') {
+							goto l183
+						}
+						position++
+					}
+				l190:
+					{
+						position192, tokenIndex192 := position, tokenIndex
+						if buffer[position] != rune('l') {
+							goto l193
+						}
+						position++
+						goto l192
+					l193:
+						position, tokenIndex = position192, tokenIndex192
+						if buffer[position] != rune('L') {
+							goto l183
+						}
+						position++
+					}
+				l192:
+					{
+						position194, tokenIndex194 := position, tokenIndex
+						if buffer[position] != rune('e') {
+							goto l195
+						}
+						position++
+						goto l194
+					l195:
+						position, tokenIndex = position194, tokenIndex194
+						if buffer[position] != rune('E') {
+							goto l183
+						}
+						position++
+					}
+				l194:
+					{
+						position196, tokenIndex196 := position, tokenIndex
+						if buffer[position] != rune('n') {
+							goto l197
+						}
+						position++
+						goto l196
+					l197:
+						position, tokenIndex = position196, tokenIndex196
+						if buffer[position] != rune('N') {
+							goto l183
+						}
+						position++
+					}
+				l196:
+					{
+						position198, tokenIndex198 := position, tokenIndex
+						if buffer[position] != rune('t') {
+							goto l199
+						}
+						position++
+						goto l198
+					l199:
+						position, tokenIndex = position198, tokenIndex198
+						if buffer[position] != rune('T') {
+							goto l183
+						}
+						position++
+					}
+				l198:
+					{
+						position200, tokenIndex200 := position, tokenIndex
+						if buffer[position] != rune('r') {
+							goto l201
+						}
+						position++
+						goto l200
+					l201:
+						position, tokenIndex = position200, tokenIndex200
+						if buffer[position] != rune('R') {
+							goto l183
+						}
+						position++
+					}
+				l200:
+					{
+						position202, tokenIndex202 := position, tokenIndex
+						if buffer[position] != rune('y') {
+							goto l203
+						}
+						position++
+						goto l202
+					l203:
+						position, tokenIndex = position202, tokenIndex202
+						if buffer[position] != rune('Y') {
+							goto l183
+						}
+						position++
+					}
+				l202:
+					goto l134
+				l183:
+					position, tokenIndex = position134, tokenIndex134
+					if buffer[position] != rune('.') {
+						goto l204
+					}
+					position++
+					{
+						position205, tokenIndex205 := position, tokenIndex
+						if buffer[position] != rune('s') {
+							goto l206
+						}
+						position++
+						goto l205
+					l206:
+						position, tokenIndex = position205, tokenIndex205
+						if buffer[position] != rune('S') {
+							goto l204
+						}
+						position++
+					}
+				l205:
+					{
+						position207, tokenIndex207 := position, tokenIndex
+						if buffer[position] != rune('i') {
+							goto l208
+						}
+						position++
+						goto l207
+					l208:
+						position, tokenIndex = position207, tokenIndex207
+						if buffer[position] != rune('I') {
+							goto l204
+						}
+						position++
+					}
+				l207:
+					{
+						position209, tokenIndex209 := position, tokenIndex
+						if buffer[position] != rune('z') {
+							goto l210
+						}
+						position++
+						goto l209
+					l210:
+						position, tokenIndex = position209, tokenIndex209
+						if buffer[position] != rune('Z') {
+							goto l204
+						}
+						position++
+					}
+				l209:
+					{
+						position211, tokenIndex211 := position, tokenIndex
+						if buffer[position] != rune('e') {
+							goto l212
+						}
+						position++
+						goto l211
+					l212:
+						position, tokenIndex = position211, tokenIndex211
+						if buffer[position] != rune('E') {
+							goto l204
+						}
+						position++
+					}
+				l211:
+					goto l134
+				l204:
+					position, tokenIndex = position134, tokenIndex134
+					if buffer[position] != rune('.') {
+						goto l132
+					}
+					position++
+					{
+						position213, tokenIndex213 := position, tokenIndex
+						if buffer[position] != rune('t') {
+							goto l214
+						}
+						position++
+						goto l213
+					l214:
+						position, tokenIndex = position213, tokenIndex213
+						if buffer[position] != rune('T') {
+							goto l132
+						}
+						position++
+					}
+				l213:
+					{
+						position215, tokenIndex215 := position, tokenIndex
+						if buffer[position] != rune('y') {
+							goto l216
+						}
+						position++
+						goto l215
+					l216:
+						position, tokenIndex = position215, tokenIndex215
+						if buffer[position] != rune('Y') {
+							goto l132
+						}
+						position++
+					}
+				l215:
+					{
+						position217, tokenIndex217 := position, tokenIndex
+						if buffer[position] != rune('p') {
+							goto l218
+						}
+						position++
+						goto l217
+					l218:
+						position, tokenIndex = position217, tokenIndex217
+						if buffer[position] != rune('P') {
+							goto l132
+						}
+						position++
+					}
+				l217:
+					{
+						position219, tokenIndex219 := position, tokenIndex
+						if buffer[position] != rune('e') {
+							goto l220
+						}
+						position++
+						goto l219
+					l220:
+						position, tokenIndex = position219, tokenIndex219
+						if buffer[position] != rune('E') {
+							goto l132
+						}
+						position++
+					}
+				l219:
+				}
+			l134:
+				add(ruleLabelContainingDirectiveName, position133)
+			}
+			return true
+		l132:
+			position, tokenIndex = position132, tokenIndex132
+			return false
+		},
+		/* 12 SymbolArgs <- <(SymbolArg (WS? ',' WS? SymbolArg)*)> */
+		func() bool {
+			position221, tokenIndex221 := position, tokenIndex
+			{
+				position222 := position
+				if !_rules[ruleSymbolArg]() {
+					goto l221
+				}
+			l223:
+				{
+					position224, tokenIndex224 := position, tokenIndex
+					{
+						position225, tokenIndex225 := position, tokenIndex
+						if !_rules[ruleWS]() {
+							goto l225
+						}
+						goto l226
+					l225:
+						position, tokenIndex = position225, tokenIndex225
+					}
+				l226:
+					if buffer[position] != rune(',') {
+						goto l224
+					}
+					position++
+					{
+						position227, tokenIndex227 := position, tokenIndex
+						if !_rules[ruleWS]() {
+							goto l227
+						}
+						goto l228
+					l227:
+						position, tokenIndex = position227, tokenIndex227
+					}
+				l228:
+					if !_rules[ruleSymbolArg]() {
+						goto l224
+					}
+					goto l223
+				l224:
+					position, tokenIndex = position224, tokenIndex224
+				}
+				add(ruleSymbolArgs, position222)
+			}
+			return true
+		l221:
+			position, tokenIndex = position221, tokenIndex221
+			return false
+		},
+		/* 13 SymbolArg <- <(Offset / SymbolType / ((Offset / LocalSymbol / SymbolName / Dot) WS? Operator WS? (Offset / LocalSymbol / SymbolName)) / (LocalSymbol TCMarker?) / (SymbolName Offset) / (SymbolName TCMarker?))> */
+		func() bool {
+			position229, tokenIndex229 := position, tokenIndex
+			{
+				position230 := position
+				{
+					position231, tokenIndex231 := position, tokenIndex
+					if !_rules[ruleOffset]() {
+						goto l232
+					}
+					goto l231
+				l232:
+					position, tokenIndex = position231, tokenIndex231
+					if !_rules[ruleSymbolType]() {
+						goto l233
+					}
+					goto l231
+				l233:
+					position, tokenIndex = position231, tokenIndex231
+					{
+						position235, tokenIndex235 := position, tokenIndex
+						if !_rules[ruleOffset]() {
+							goto l236
+						}
+						goto l235
+					l236:
+						position, tokenIndex = position235, tokenIndex235
+						if !_rules[ruleLocalSymbol]() {
+							goto l237
+						}
+						goto l235
+					l237:
+						position, tokenIndex = position235, tokenIndex235
+						if !_rules[ruleSymbolName]() {
+							goto l238
+						}
+						goto l235
+					l238:
+						position, tokenIndex = position235, tokenIndex235
+						if !_rules[ruleDot]() {
+							goto l234
+						}
+					}
+				l235:
+					{
+						position239, tokenIndex239 := position, tokenIndex
+						if !_rules[ruleWS]() {
+							goto l239
+						}
+						goto l240
+					l239:
+						position, tokenIndex = position239, tokenIndex239
+					}
+				l240:
+					if !_rules[ruleOperator]() {
+						goto l234
+					}
+					{
+						position241, tokenIndex241 := position, tokenIndex
+						if !_rules[ruleWS]() {
+							goto l241
+						}
+						goto l242
+					l241:
+						position, tokenIndex = position241, tokenIndex241
+					}
+				l242:
+					{
+						position243, tokenIndex243 := position, tokenIndex
+						if !_rules[ruleOffset]() {
+							goto l244
+						}
+						goto l243
+					l244:
+						position, tokenIndex = position243, tokenIndex243
+						if !_rules[ruleLocalSymbol]() {
+							goto l245
+						}
+						goto l243
+					l245:
+						position, tokenIndex = position243, tokenIndex243
+						if !_rules[ruleSymbolName]() {
+							goto l234
+						}
+					}
+				l243:
+					goto l231
+				l234:
+					position, tokenIndex = position231, tokenIndex231
+					if !_rules[ruleLocalSymbol]() {
+						goto l246
+					}
+					{
+						position247, tokenIndex247 := position, tokenIndex
+						if !_rules[ruleTCMarker]() {
+							goto l247
+						}
+						goto l248
+					l247:
+						position, tokenIndex = position247, tokenIndex247
+					}
+				l248:
+					goto l231
+				l246:
+					position, tokenIndex = position231, tokenIndex231
+					if !_rules[ruleSymbolName]() {
+						goto l249
+					}
+					if !_rules[ruleOffset]() {
+						goto l249
+					}
+					goto l231
+				l249:
+					position, tokenIndex = position231, tokenIndex231
+					if !_rules[ruleSymbolName]() {
+						goto l229
+					}
+					{
+						position250, tokenIndex250 := position, tokenIndex
+						if !_rules[ruleTCMarker]() {
+							goto l250
+						}
+						goto l251
+					l250:
+						position, tokenIndex = position250, tokenIndex250
+					}
+				l251:
+				}
+			l231:
+				add(ruleSymbolArg, position230)
+			}
+			return true
+		l229:
+			position, tokenIndex = position229, tokenIndex229
+			return false
+		},
+		/* 14 SymbolType <- <(('@' 'f' 'u' 'n' 'c' 't' 'i' 'o' 'n') / ('@' 'o' 'b' 'j' 'e' 'c' 't'))> */
+		func() bool {
+			position252, tokenIndex252 := position, tokenIndex
+			{
+				position253 := position
+				{
+					position254, tokenIndex254 := position, tokenIndex
+					if buffer[position] != rune('@') {
+						goto l255
+					}
+					position++
+					if buffer[position] != rune('f') {
+						goto l255
+					}
+					position++
+					if buffer[position] != rune('u') {
+						goto l255
+					}
+					position++
+					if buffer[position] != rune('n') {
+						goto l255
+					}
+					position++
+					if buffer[position] != rune('c') {
+						goto l255
+					}
+					position++
+					if buffer[position] != rune('t') {
+						goto l255
+					}
+					position++
+					if buffer[position] != rune('i') {
+						goto l255
+					}
+					position++
+					if buffer[position] != rune('o') {
+						goto l255
+					}
+					position++
+					if buffer[position] != rune('n') {
+						goto l255
+					}
+					position++
+					goto l254
+				l255:
+					position, tokenIndex = position254, tokenIndex254
+					if buffer[position] != rune('@') {
+						goto l252
+					}
+					position++
+					if buffer[position] != rune('o') {
+						goto l252
+					}
+					position++
+					if buffer[position] != rune('b') {
+						goto l252
+					}
+					position++
+					if buffer[position] != rune('j') {
+						goto l252
+					}
+					position++
+					if buffer[position] != rune('e') {
+						goto l252
+					}
+					position++
+					if buffer[position] != rune('c') {
+						goto l252
+					}
+					position++
+					if buffer[position] != rune('t') {
+						goto l252
+					}
+					position++
+				}
+			l254:
+				add(ruleSymbolType, position253)
+			}
+			return true
+		l252:
+			position, tokenIndex = position252, tokenIndex252
+			return false
+		},
+		/* 15 Dot <- <'.'> */
+		func() bool {
+			position256, tokenIndex256 := position, tokenIndex
+			{
+				position257 := position
+				if buffer[position] != rune('.') {
+					goto l256
+				}
+				position++
+				add(ruleDot, position257)
+			}
+			return true
+		l256:
+			position, tokenIndex = position256, tokenIndex256
+			return false
+		},
+		/* 16 TCMarker <- <('[' 'T' 'C' ']')> */
+		func() bool {
+			position258, tokenIndex258 := position, tokenIndex
+			{
+				position259 := position
+				if buffer[position] != rune('[') {
+					goto l258
+				}
+				position++
+				if buffer[position] != rune('T') {
+					goto l258
+				}
+				position++
+				if buffer[position] != rune('C') {
+					goto l258
+				}
+				position++
+				if buffer[position] != rune(']') {
+					goto l258
+				}
+				position++
+				add(ruleTCMarker, position259)
+			}
+			return true
+		l258:
+			position, tokenIndex = position258, tokenIndex258
+			return false
+		},
+		/* 17 EscapedChar <- <('\\' .)> */
+		func() bool {
+			position260, tokenIndex260 := position, tokenIndex
+			{
+				position261 := position
+				if buffer[position] != rune('\\') {
+					goto l260
+				}
+				position++
+				if !matchDot() {
+					goto l260
+				}
+				add(ruleEscapedChar, position261)
+			}
+			return true
+		l260:
+			position, tokenIndex = position260, tokenIndex260
+			return false
+		},
+		/* 18 WS <- <(' ' / '\t')+> */
+		func() bool {
+			position262, tokenIndex262 := position, tokenIndex
+			{
+				position263 := position
+				{
+					position266, tokenIndex266 := position, tokenIndex
+					if buffer[position] != rune(' ') {
+						goto l267
+					}
+					position++
+					goto l266
+				l267:
+					position, tokenIndex = position266, tokenIndex266
+					if buffer[position] != rune('\t') {
+						goto l262
+					}
+					position++
+				}
+			l266:
+			l264:
+				{
+					position265, tokenIndex265 := position, tokenIndex
+					{
+						position268, tokenIndex268 := position, tokenIndex
+						if buffer[position] != rune(' ') {
+							goto l269
+						}
+						position++
+						goto l268
+					l269:
+						position, tokenIndex = position268, tokenIndex268
+						if buffer[position] != rune('\t') {
+							goto l265
+						}
+						position++
+					}
+				l268:
+					goto l264
+				l265:
+					position, tokenIndex = position265, tokenIndex265
+				}
+				add(ruleWS, position263)
+			}
+			return true
+		l262:
+			position, tokenIndex = position262, tokenIndex262
+			return false
+		},
+		/* 19 Comment <- <('#' (!'\n' .)*)> */
+		func() bool {
+			position270, tokenIndex270 := position, tokenIndex
+			{
+				position271 := position
+				if buffer[position] != rune('#') {
+					goto l270
+				}
+				position++
+			l272:
+				{
+					position273, tokenIndex273 := position, tokenIndex
+					{
+						position274, tokenIndex274 := position, tokenIndex
+						if buffer[position] != rune('\n') {
+							goto l274
+						}
+						position++
+						goto l273
+					l274:
+						position, tokenIndex = position274, tokenIndex274
+					}
+					if !matchDot() {
+						goto l273
+					}
+					goto l272
+				l273:
+					position, tokenIndex = position273, tokenIndex273
+				}
+				add(ruleComment, position271)
+			}
+			return true
+		l270:
+			position, tokenIndex = position270, tokenIndex270
+			return false
+		},
+		/* 20 Label <- <((LocalSymbol / LocalLabel / SymbolName) ':')> */
+		func() bool {
+			position275, tokenIndex275 := position, tokenIndex
+			{
+				position276 := position
+				{
+					position277, tokenIndex277 := position, tokenIndex
+					if !_rules[ruleLocalSymbol]() {
+						goto l278
+					}
+					goto l277
+				l278:
+					position, tokenIndex = position277, tokenIndex277
+					if !_rules[ruleLocalLabel]() {
+						goto l279
+					}
+					goto l277
+				l279:
+					position, tokenIndex = position277, tokenIndex277
+					if !_rules[ruleSymbolName]() {
+						goto l275
+					}
+				}
+			l277:
+				if buffer[position] != rune(':') {
+					goto l275
+				}
+				position++
+				add(ruleLabel, position276)
+			}
+			return true
+		l275:
+			position, tokenIndex = position275, tokenIndex275
+			return false
+		},
+		/* 21 SymbolName <- <(([a-z] / [A-Z] / '.' / '_') ([a-z] / [A-Z] / '.' / ([0-9] / [0-9]) / '$' / '_')*)> */
+		func() bool {
+			position280, tokenIndex280 := position, tokenIndex
+			{
+				position281 := position
+				{
+					position282, tokenIndex282 := position, tokenIndex
+					if c := buffer[position]; c < rune('a') || c > rune('z') {
+						goto l283
+					}
+					position++
+					goto l282
+				l283:
+					position, tokenIndex = position282, tokenIndex282
+					if c := buffer[position]; c < rune('A') || c > rune('Z') {
+						goto l284
+					}
+					position++
+					goto l282
+				l284:
+					position, tokenIndex = position282, tokenIndex282
+					if buffer[position] != rune('.') {
+						goto l285
+					}
+					position++
+					goto l282
+				l285:
+					position, tokenIndex = position282, tokenIndex282
+					if buffer[position] != rune('_') {
+						goto l280
+					}
+					position++
+				}
+			l282:
+			l286:
+				{
+					position287, tokenIndex287 := position, tokenIndex
+					{
+						position288, tokenIndex288 := position, tokenIndex
+						if c := buffer[position]; c < rune('a') || c > rune('z') {
+							goto l289
+						}
+						position++
+						goto l288
+					l289:
+						position, tokenIndex = position288, tokenIndex288
+						if c := buffer[position]; c < rune('A') || c > rune('Z') {
+							goto l290
+						}
+						position++
+						goto l288
+					l290:
+						position, tokenIndex = position288, tokenIndex288
+						if buffer[position] != rune('.') {
+							goto l291
+						}
+						position++
+						goto l288
+					l291:
+						position, tokenIndex = position288, tokenIndex288
+						{
+							position293, tokenIndex293 := position, tokenIndex
+							if c := buffer[position]; c < rune('0') || c > rune('9') {
+								goto l294
+							}
+							position++
+							goto l293
+						l294:
+							position, tokenIndex = position293, tokenIndex293
+							if c := buffer[position]; c < rune('0') || c > rune('9') {
+								goto l292
+							}
+							position++
+						}
+					l293:
+						goto l288
+					l292:
+						position, tokenIndex = position288, tokenIndex288
+						if buffer[position] != rune('$') {
+							goto l295
+						}
+						position++
+						goto l288
+					l295:
+						position, tokenIndex = position288, tokenIndex288
+						if buffer[position] != rune('_') {
+							goto l287
+						}
+						position++
+					}
+				l288:
+					goto l286
+				l287:
+					position, tokenIndex = position287, tokenIndex287
+				}
+				add(ruleSymbolName, position281)
+			}
+			return true
+		l280:
+			position, tokenIndex = position280, tokenIndex280
+			return false
+		},
+		/* 22 LocalSymbol <- <('.' 'L' ([a-z] / [A-Z] / '.' / ([0-9] / [0-9]) / '$' / '_')+)> */
+		func() bool {
+			position296, tokenIndex296 := position, tokenIndex
+			{
+				position297 := position
+				if buffer[position] != rune('.') {
+					goto l296
+				}
+				position++
+				if buffer[position] != rune('L') {
+					goto l296
+				}
+				position++
+				{
+					position300, tokenIndex300 := position, tokenIndex
+					if c := buffer[position]; c < rune('a') || c > rune('z') {
+						goto l301
+					}
+					position++
+					goto l300
+				l301:
+					position, tokenIndex = position300, tokenIndex300
+					if c := buffer[position]; c < rune('A') || c > rune('Z') {
+						goto l302
+					}
+					position++
+					goto l300
+				l302:
+					position, tokenIndex = position300, tokenIndex300
+					if buffer[position] != rune('.') {
+						goto l303
+					}
+					position++
+					goto l300
+				l303:
+					position, tokenIndex = position300, tokenIndex300
+					{
+						position305, tokenIndex305 := position, tokenIndex
+						if c := buffer[position]; c < rune('0') || c > rune('9') {
+							goto l306
+						}
+						position++
+						goto l305
+					l306:
+						position, tokenIndex = position305, tokenIndex305
+						if c := buffer[position]; c < rune('0') || c > rune('9') {
+							goto l304
+						}
+						position++
+					}
+				l305:
+					goto l300
+				l304:
+					position, tokenIndex = position300, tokenIndex300
+					if buffer[position] != rune('$') {
+						goto l307
+					}
+					position++
+					goto l300
+				l307:
+					position, tokenIndex = position300, tokenIndex300
+					if buffer[position] != rune('_') {
+						goto l296
+					}
+					position++
+				}
+			l300:
+			l298:
+				{
+					position299, tokenIndex299 := position, tokenIndex
+					{
+						position308, tokenIndex308 := position, tokenIndex
+						if c := buffer[position]; c < rune('a') || c > rune('z') {
+							goto l309
+						}
+						position++
+						goto l308
+					l309:
+						position, tokenIndex = position308, tokenIndex308
+						if c := buffer[position]; c < rune('A') || c > rune('Z') {
+							goto l310
+						}
+						position++
+						goto l308
+					l310:
+						position, tokenIndex = position308, tokenIndex308
+						if buffer[position] != rune('.') {
+							goto l311
+						}
+						position++
+						goto l308
+					l311:
+						position, tokenIndex = position308, tokenIndex308
+						{
+							position313, tokenIndex313 := position, tokenIndex
+							if c := buffer[position]; c < rune('0') || c > rune('9') {
+								goto l314
+							}
+							position++
+							goto l313
+						l314:
+							position, tokenIndex = position313, tokenIndex313
+							if c := buffer[position]; c < rune('0') || c > rune('9') {
+								goto l312
+							}
+							position++
+						}
+					l313:
+						goto l308
+					l312:
+						position, tokenIndex = position308, tokenIndex308
+						if buffer[position] != rune('$') {
+							goto l315
+						}
+						position++
+						goto l308
+					l315:
+						position, tokenIndex = position308, tokenIndex308
+						if buffer[position] != rune('_') {
+							goto l299
+						}
+						position++
+					}
+				l308:
+					goto l298
+				l299:
+					position, tokenIndex = position299, tokenIndex299
+				}
+				add(ruleLocalSymbol, position297)
+			}
+			return true
+		l296:
+			position, tokenIndex = position296, tokenIndex296
+			return false
+		},
+		/* 23 LocalLabel <- <([0-9] ([0-9] / '$')*)> */
+		func() bool {
+			position316, tokenIndex316 := position, tokenIndex
+			{
+				position317 := position
+				if c := buffer[position]; c < rune('0') || c > rune('9') {
+					goto l316
+				}
+				position++
+			l318:
+				{
+					position319, tokenIndex319 := position, tokenIndex
+					{
+						position320, tokenIndex320 := position, tokenIndex
+						if c := buffer[position]; c < rune('0') || c > rune('9') {
+							goto l321
+						}
+						position++
+						goto l320
+					l321:
+						position, tokenIndex = position320, tokenIndex320
+						if buffer[position] != rune('$') {
+							goto l319
+						}
+						position++
+					}
+				l320:
+					goto l318
+				l319:
+					position, tokenIndex = position319, tokenIndex319
+				}
+				add(ruleLocalLabel, position317)
+			}
+			return true
+		l316:
+			position, tokenIndex = position316, tokenIndex316
+			return false
+		},
+		/* 24 LocalLabelRef <- <([0-9] ([0-9] / '$')* ('b' / 'f'))> */
+		func() bool {
+			position322, tokenIndex322 := position, tokenIndex
+			{
+				position323 := position
+				if c := buffer[position]; c < rune('0') || c > rune('9') {
+					goto l322
+				}
+				position++
+			l324:
+				{
+					position325, tokenIndex325 := position, tokenIndex
+					{
+						position326, tokenIndex326 := position, tokenIndex
+						if c := buffer[position]; c < rune('0') || c > rune('9') {
+							goto l327
+						}
+						position++
+						goto l326
+					l327:
+						position, tokenIndex = position326, tokenIndex326
+						if buffer[position] != rune('$') {
+							goto l325
+						}
+						position++
+					}
+				l326:
+					goto l324
+				l325:
+					position, tokenIndex = position325, tokenIndex325
+				}
+				{
+					position328, tokenIndex328 := position, tokenIndex
+					if buffer[position] != rune('b') {
+						goto l329
+					}
+					position++
+					goto l328
+				l329:
+					position, tokenIndex = position328, tokenIndex328
+					if buffer[position] != rune('f') {
+						goto l322
+					}
+					position++
+				}
+			l328:
+				add(ruleLocalLabelRef, position323)
+			}
+			return true
+		l322:
+			position, tokenIndex = position322, tokenIndex322
+			return false
+		},
+		/* 25 Instruction <- <(InstructionName (WS InstructionArg (WS? ',' WS? InstructionArg)*)?)> */
+		func() bool {
+			position330, tokenIndex330 := position, tokenIndex
+			{
+				position331 := position
+				if !_rules[ruleInstructionName]() {
+					goto l330
+				}
+				{
+					position332, tokenIndex332 := position, tokenIndex
+					if !_rules[ruleWS]() {
+						goto l332
+					}
+					if !_rules[ruleInstructionArg]() {
+						goto l332
+					}
+				l334:
+					{
+						position335, tokenIndex335 := position, tokenIndex
+						{
+							position336, tokenIndex336 := position, tokenIndex
+							if !_rules[ruleWS]() {
+								goto l336
+							}
+							goto l337
+						l336:
+							position, tokenIndex = position336, tokenIndex336
+						}
+					l337:
+						if buffer[position] != rune(',') {
+							goto l335
+						}
+						position++
+						{
+							position338, tokenIndex338 := position, tokenIndex
+							if !_rules[ruleWS]() {
+								goto l338
+							}
+							goto l339
+						l338:
+							position, tokenIndex = position338, tokenIndex338
+						}
+					l339:
+						if !_rules[ruleInstructionArg]() {
+							goto l335
+						}
+						goto l334
+					l335:
+						position, tokenIndex = position335, tokenIndex335
+					}
+					goto l333
+				l332:
+					position, tokenIndex = position332, tokenIndex332
+				}
+			l333:
+				add(ruleInstruction, position331)
+			}
+			return true
+		l330:
+			position, tokenIndex = position330, tokenIndex330
+			return false
+		},
+		/* 26 InstructionName <- <(([a-z] / [A-Z]) ([a-z] / [A-Z] / ([0-9] / [0-9]))* ('.' / '+' / '-')?)> */
+		func() bool {
+			position340, tokenIndex340 := position, tokenIndex
+			{
+				position341 := position
+				{
+					position342, tokenIndex342 := position, tokenIndex
+					if c := buffer[position]; c < rune('a') || c > rune('z') {
+						goto l343
+					}
+					position++
+					goto l342
+				l343:
+					position, tokenIndex = position342, tokenIndex342
+					if c := buffer[position]; c < rune('A') || c > rune('Z') {
+						goto l340
+					}
+					position++
+				}
+			l342:
+			l344:
+				{
+					position345, tokenIndex345 := position, tokenIndex
+					{
+						position346, tokenIndex346 := position, tokenIndex
+						if c := buffer[position]; c < rune('a') || c > rune('z') {
+							goto l347
+						}
+						position++
+						goto l346
+					l347:
+						position, tokenIndex = position346, tokenIndex346
+						if c := buffer[position]; c < rune('A') || c > rune('Z') {
+							goto l348
+						}
+						position++
+						goto l346
+					l348:
+						position, tokenIndex = position346, tokenIndex346
+						{
+							position349, tokenIndex349 := position, tokenIndex
+							if c := buffer[position]; c < rune('0') || c > rune('9') {
+								goto l350
+							}
+							position++
+							goto l349
+						l350:
+							position, tokenIndex = position349, tokenIndex349
+							if c := buffer[position]; c < rune('0') || c > rune('9') {
+								goto l345
+							}
+							position++
+						}
+					l349:
+					}
+				l346:
+					goto l344
+				l345:
+					position, tokenIndex = position345, tokenIndex345
+				}
+				{
+					position351, tokenIndex351 := position, tokenIndex
+					{
+						position353, tokenIndex353 := position, tokenIndex
+						if buffer[position] != rune('.') {
+							goto l354
+						}
+						position++
+						goto l353
+					l354:
+						position, tokenIndex = position353, tokenIndex353
+						if buffer[position] != rune('+') {
+							goto l355
+						}
+						position++
+						goto l353
+					l355:
+						position, tokenIndex = position353, tokenIndex353
+						if buffer[position] != rune('-') {
+							goto l351
+						}
+						position++
+					}
+				l353:
+					goto l352
+				l351:
+					position, tokenIndex = position351, tokenIndex351
+				}
+			l352:
+				add(ruleInstructionName, position341)
+			}
+			return true
+		l340:
+			position, tokenIndex = position340, tokenIndex340
+			return false
+		},
+		/* 27 InstructionArg <- <(IndirectionIndicator? (RegisterOrConstant / LocalLabelRef / TOCRefHigh / TOCRefLow / MemoryRef))> */
+		func() bool {
+			position356, tokenIndex356 := position, tokenIndex
+			{
+				position357 := position
+				{
+					position358, tokenIndex358 := position, tokenIndex
+					if !_rules[ruleIndirectionIndicator]() {
+						goto l358
+					}
+					goto l359
+				l358:
+					position, tokenIndex = position358, tokenIndex358
+				}
+			l359:
+				{
+					position360, tokenIndex360 := position, tokenIndex
+					if !_rules[ruleRegisterOrConstant]() {
+						goto l361
+					}
+					goto l360
+				l361:
+					position, tokenIndex = position360, tokenIndex360
+					if !_rules[ruleLocalLabelRef]() {
+						goto l362
+					}
+					goto l360
+				l362:
+					position, tokenIndex = position360, tokenIndex360
+					if !_rules[ruleTOCRefHigh]() {
+						goto l363
+					}
+					goto l360
+				l363:
+					position, tokenIndex = position360, tokenIndex360
+					if !_rules[ruleTOCRefLow]() {
+						goto l364
+					}
+					goto l360
+				l364:
+					position, tokenIndex = position360, tokenIndex360
+					if !_rules[ruleMemoryRef]() {
+						goto l356
+					}
+				}
+			l360:
+				add(ruleInstructionArg, position357)
+			}
+			return true
+		l356:
+			position, tokenIndex = position356, tokenIndex356
+			return false
+		},
+		/* 28 TOCRefHigh <- <('.' 'T' 'O' 'C' '.' '-' (('0' 'b') / ('.' 'L' 'f' 'u' 'n' 'c' '_' 'g' 'e' 'p' [0-9]+)) ('@' ('h' / 'H') ('a' / 'A')))> */
+		func() bool {
+			position365, tokenIndex365 := position, tokenIndex
+			{
+				position366 := position
+				if buffer[position] != rune('.') {
+					goto l365
+				}
+				position++
+				if buffer[position] != rune('T') {
+					goto l365
+				}
+				position++
+				if buffer[position] != rune('O') {
+					goto l365
+				}
+				position++
+				if buffer[position] != rune('C') {
+					goto l365
+				}
+				position++
+				if buffer[position] != rune('.') {
+					goto l365
+				}
+				position++
+				if buffer[position] != rune('-') {
+					goto l365
+				}
+				position++
+				{
+					position367, tokenIndex367 := position, tokenIndex
+					if buffer[position] != rune('0') {
+						goto l368
+					}
+					position++
+					if buffer[position] != rune('b') {
+						goto l368
+					}
+					position++
+					goto l367
+				l368:
+					position, tokenIndex = position367, tokenIndex367
+					if buffer[position] != rune('.') {
+						goto l365
+					}
+					position++
+					if buffer[position] != rune('L') {
+						goto l365
+					}
+					position++
+					if buffer[position] != rune('f') {
+						goto l365
+					}
+					position++
+					if buffer[position] != rune('u') {
+						goto l365
+					}
+					position++
+					if buffer[position] != rune('n') {
+						goto l365
+					}
+					position++
+					if buffer[position] != rune('c') {
+						goto l365
+					}
+					position++
+					if buffer[position] != rune('_') {
+						goto l365
+					}
+					position++
+					if buffer[position] != rune('g') {
+						goto l365
+					}
+					position++
+					if buffer[position] != rune('e') {
+						goto l365
+					}
+					position++
+					if buffer[position] != rune('p') {
+						goto l365
+					}
+					position++
+					if c := buffer[position]; c < rune('0') || c > rune('9') {
+						goto l365
+					}
+					position++
+				l369:
+					{
+						position370, tokenIndex370 := position, tokenIndex
+						if c := buffer[position]; c < rune('0') || c > rune('9') {
+							goto l370
+						}
+						position++
+						goto l369
+					l370:
+						position, tokenIndex = position370, tokenIndex370
+					}
+				}
+			l367:
+				if buffer[position] != rune('@') {
+					goto l365
+				}
+				position++
+				{
+					position371, tokenIndex371 := position, tokenIndex
+					if buffer[position] != rune('h') {
+						goto l372
+					}
+					position++
+					goto l371
+				l372:
+					position, tokenIndex = position371, tokenIndex371
+					if buffer[position] != rune('H') {
+						goto l365
+					}
+					position++
+				}
+			l371:
+				{
+					position373, tokenIndex373 := position, tokenIndex
+					if buffer[position] != rune('a') {
+						goto l374
+					}
+					position++
+					goto l373
+				l374:
+					position, tokenIndex = position373, tokenIndex373
+					if buffer[position] != rune('A') {
+						goto l365
+					}
+					position++
+				}
+			l373:
+				add(ruleTOCRefHigh, position366)
+			}
+			return true
+		l365:
+			position, tokenIndex = position365, tokenIndex365
+			return false
+		},
+		/* 29 TOCRefLow <- <('.' 'T' 'O' 'C' '.' '-' (('0' 'b') / ('.' 'L' 'f' 'u' 'n' 'c' '_' 'g' 'e' 'p' [0-9]+)) ('@' ('l' / 'L')))> */
+		func() bool {
+			position375, tokenIndex375 := position, tokenIndex
+			{
+				position376 := position
+				if buffer[position] != rune('.') {
+					goto l375
+				}
+				position++
+				if buffer[position] != rune('T') {
+					goto l375
+				}
+				position++
+				if buffer[position] != rune('O') {
+					goto l375
+				}
+				position++
+				if buffer[position] != rune('C') {
+					goto l375
+				}
+				position++
+				if buffer[position] != rune('.') {
+					goto l375
+				}
+				position++
+				if buffer[position] != rune('-') {
+					goto l375
+				}
+				position++
+				{
+					position377, tokenIndex377 := position, tokenIndex
+					if buffer[position] != rune('0') {
+						goto l378
+					}
+					position++
+					if buffer[position] != rune('b') {
+						goto l378
+					}
+					position++
+					goto l377
+				l378:
+					position, tokenIndex = position377, tokenIndex377
+					if buffer[position] != rune('.') {
+						goto l375
+					}
+					position++
+					if buffer[position] != rune('L') {
+						goto l375
+					}
+					position++
+					if buffer[position] != rune('f') {
+						goto l375
+					}
+					position++
+					if buffer[position] != rune('u') {
+						goto l375
+					}
+					position++
+					if buffer[position] != rune('n') {
+						goto l375
+					}
+					position++
+					if buffer[position] != rune('c') {
+						goto l375
+					}
+					position++
+					if buffer[position] != rune('_') {
+						goto l375
+					}
+					position++
+					if buffer[position] != rune('g') {
+						goto l375
+					}
+					position++
+					if buffer[position] != rune('e') {
+						goto l375
+					}
+					position++
+					if buffer[position] != rune('p') {
+						goto l375
+					}
+					position++
+					if c := buffer[position]; c < rune('0') || c > rune('9') {
+						goto l375
+					}
+					position++
+				l379:
+					{
+						position380, tokenIndex380 := position, tokenIndex
+						if c := buffer[position]; c < rune('0') || c > rune('9') {
+							goto l380
+						}
+						position++
+						goto l379
+					l380:
+						position, tokenIndex = position380, tokenIndex380
+					}
+				}
+			l377:
+				if buffer[position] != rune('@') {
+					goto l375
+				}
+				position++
+				{
+					position381, tokenIndex381 := position, tokenIndex
+					if buffer[position] != rune('l') {
+						goto l382
+					}
+					position++
+					goto l381
+				l382:
+					position, tokenIndex = position381, tokenIndex381
+					if buffer[position] != rune('L') {
+						goto l375
+					}
+					position++
+				}
+			l381:
+				add(ruleTOCRefLow, position376)
+			}
+			return true
+		l375:
+			position, tokenIndex = position375, tokenIndex375
+			return false
+		},
+		/* 30 IndirectionIndicator <- <'*'> */
+		func() bool {
+			position383, tokenIndex383 := position, tokenIndex
+			{
+				position384 := position
+				if buffer[position] != rune('*') {
+					goto l383
+				}
+				position++
+				add(ruleIndirectionIndicator, position384)
+			}
+			return true
+		l383:
+			position, tokenIndex = position383, tokenIndex383
+			return false
+		},
+		/* 31 RegisterOrConstant <- <((('%' ([a-z] / [A-Z]) ([a-z] / [A-Z] / ([0-9] / [0-9]))*) / ('$'? ((Offset Offset) / Offset))) !('f' / 'b' / ':' / '(' / '+' / '-'))> */
+		func() bool {
+			position385, tokenIndex385 := position, tokenIndex
+			{
+				position386 := position
+				{
+					position387, tokenIndex387 := position, tokenIndex
+					if buffer[position] != rune('%') {
+						goto l388
+					}
+					position++
+					{
+						position389, tokenIndex389 := position, tokenIndex
+						if c := buffer[position]; c < rune('a') || c > rune('z') {
+							goto l390
+						}
+						position++
+						goto l389
+					l390:
+						position, tokenIndex = position389, tokenIndex389
+						if c := buffer[position]; c < rune('A') || c > rune('Z') {
+							goto l388
+						}
+						position++
+					}
+				l389:
+				l391:
+					{
+						position392, tokenIndex392 := position, tokenIndex
+						{
+							position393, tokenIndex393 := position, tokenIndex
+							if c := buffer[position]; c < rune('a') || c > rune('z') {
+								goto l394
+							}
+							position++
+							goto l393
+						l394:
+							position, tokenIndex = position393, tokenIndex393
+							if c := buffer[position]; c < rune('A') || c > rune('Z') {
+								goto l395
+							}
+							position++
+							goto l393
+						l395:
+							position, tokenIndex = position393, tokenIndex393
+							{
+								position396, tokenIndex396 := position, tokenIndex
+								if c := buffer[position]; c < rune('0') || c > rune('9') {
+									goto l397
+								}
+								position++
+								goto l396
+							l397:
+								position, tokenIndex = position396, tokenIndex396
+								if c := buffer[position]; c < rune('0') || c > rune('9') {
+									goto l392
+								}
+								position++
+							}
+						l396:
+						}
+					l393:
+						goto l391
+					l392:
+						position, tokenIndex = position392, tokenIndex392
+					}
+					goto l387
+				l388:
+					position, tokenIndex = position387, tokenIndex387
+					{
+						position398, tokenIndex398 := position, tokenIndex
+						if buffer[position] != rune('$') {
+							goto l398
+						}
+						position++
+						goto l399
+					l398:
+						position, tokenIndex = position398, tokenIndex398
+					}
+				l399:
+					{
+						position400, tokenIndex400 := position, tokenIndex
+						if !_rules[ruleOffset]() {
+							goto l401
+						}
+						if !_rules[ruleOffset]() {
+							goto l401
+						}
+						goto l400
+					l401:
+						position, tokenIndex = position400, tokenIndex400
+						if !_rules[ruleOffset]() {
+							goto l385
+						}
+					}
+				l400:
+				}
+			l387:
+				{
+					position402, tokenIndex402 := position, tokenIndex
+					{
+						position403, tokenIndex403 := position, tokenIndex
+						if buffer[position] != rune('f') {
+							goto l404
+						}
+						position++
+						goto l403
+					l404:
+						position, tokenIndex = position403, tokenIndex403
+						if buffer[position] != rune('b') {
+							goto l405
+						}
+						position++
+						goto l403
+					l405:
+						position, tokenIndex = position403, tokenIndex403
+						if buffer[position] != rune(':') {
+							goto l406
+						}
+						position++
+						goto l403
+					l406:
+						position, tokenIndex = position403, tokenIndex403
+						if buffer[position] != rune('(') {
+							goto l407
+						}
+						position++
+						goto l403
+					l407:
+						position, tokenIndex = position403, tokenIndex403
+						if buffer[position] != rune('+') {
+							goto l408
+						}
+						position++
+						goto l403
+					l408:
+						position, tokenIndex = position403, tokenIndex403
+						if buffer[position] != rune('-') {
+							goto l402
+						}
+						position++
+					}
+				l403:
+					goto l385
+				l402:
+					position, tokenIndex = position402, tokenIndex402
+				}
+				add(ruleRegisterOrConstant, position386)
+			}
+			return true
+		l385:
+			position, tokenIndex = position385, tokenIndex385
+			return false
+		},
+		/* 32 MemoryRef <- <((SymbolRef BaseIndexScale) / (SymbolRef Offset BaseIndexScale) / SymbolRef / (Offset BaseIndexScale) / (Offset Offset BaseIndexScale) / (Offset Offset Offset BaseIndexScale) / (SegmentRegister Offset BaseIndexScale) / (SegmentRegister BaseIndexScale) / (SegmentRegister Offset) / BaseIndexScale)> */
+		func() bool {
+			position409, tokenIndex409 := position, tokenIndex
+			{
+				position410 := position
+				{
+					position411, tokenIndex411 := position, tokenIndex
+					if !_rules[ruleSymbolRef]() {
+						goto l412
+					}
+					if !_rules[ruleBaseIndexScale]() {
+						goto l412
+					}
+					goto l411
+				l412:
+					position, tokenIndex = position411, tokenIndex411
+					if !_rules[ruleSymbolRef]() {
+						goto l413
+					}
+					if !_rules[ruleOffset]() {
+						goto l413
+					}
+					if !_rules[ruleBaseIndexScale]() {
+						goto l413
+					}
+					goto l411
+				l413:
+					position, tokenIndex = position411, tokenIndex411
+					if !_rules[ruleSymbolRef]() {
+						goto l414
+					}
+					goto l411
+				l414:
+					position, tokenIndex = position411, tokenIndex411
+					if !_rules[ruleOffset]() {
+						goto l415
+					}
+					if !_rules[ruleBaseIndexScale]() {
+						goto l415
+					}
+					goto l411
+				l415:
+					position, tokenIndex = position411, tokenIndex411
+					if !_rules[ruleOffset]() {
+						goto l416
+					}
+					if !_rules[ruleOffset]() {
+						goto l416
+					}
+					if !_rules[ruleBaseIndexScale]() {
+						goto l416
+					}
+					goto l411
+				l416:
+					position, tokenIndex = position411, tokenIndex411
+					if !_rules[ruleOffset]() {
+						goto l417
+					}
+					if !_rules[ruleOffset]() {
+						goto l417
+					}
+					if !_rules[ruleOffset]() {
+						goto l417
+					}
+					if !_rules[ruleBaseIndexScale]() {
+						goto l417
+					}
+					goto l411
+				l417:
+					position, tokenIndex = position411, tokenIndex411
+					if !_rules[ruleSegmentRegister]() {
+						goto l418
+					}
+					if !_rules[ruleOffset]() {
+						goto l418
+					}
+					if !_rules[ruleBaseIndexScale]() {
+						goto l418
+					}
+					goto l411
+				l418:
+					position, tokenIndex = position411, tokenIndex411
+					if !_rules[ruleSegmentRegister]() {
+						goto l419
+					}
+					if !_rules[ruleBaseIndexScale]() {
+						goto l419
+					}
+					goto l411
+				l419:
+					position, tokenIndex = position411, tokenIndex411
+					if !_rules[ruleSegmentRegister]() {
+						goto l420
+					}
+					if !_rules[ruleOffset]() {
+						goto l420
+					}
+					goto l411
+				l420:
+					position, tokenIndex = position411, tokenIndex411
+					if !_rules[ruleBaseIndexScale]() {
+						goto l409
+					}
+				}
+			l411:
+				add(ruleMemoryRef, position410)
+			}
+			return true
+		l409:
+			position, tokenIndex = position409, tokenIndex409
+			return false
+		},
+		/* 33 SymbolRef <- <((Offset '+')? (LocalSymbol / SymbolName) Offset? ('@' Section)?)> */
+		func() bool {
+			position421, tokenIndex421 := position, tokenIndex
+			{
+				position422 := position
+				{
+					position423, tokenIndex423 := position, tokenIndex
+					if !_rules[ruleOffset]() {
+						goto l423
+					}
+					if buffer[position] != rune('+') {
+						goto l423
+					}
+					position++
+					goto l424
+				l423:
+					position, tokenIndex = position423, tokenIndex423
+				}
+			l424:
+				{
+					position425, tokenIndex425 := position, tokenIndex
+					if !_rules[ruleLocalSymbol]() {
+						goto l426
+					}
+					goto l425
+				l426:
+					position, tokenIndex = position425, tokenIndex425
+					if !_rules[ruleSymbolName]() {
+						goto l421
+					}
+				}
+			l425:
+				{
+					position427, tokenIndex427 := position, tokenIndex
+					if !_rules[ruleOffset]() {
+						goto l427
+					}
+					goto l428
+				l427:
+					position, tokenIndex = position427, tokenIndex427
+				}
+			l428:
+				{
+					position429, tokenIndex429 := position, tokenIndex
+					if buffer[position] != rune('@') {
+						goto l429
+					}
+					position++
+					if !_rules[ruleSection]() {
+						goto l429
+					}
+					goto l430
+				l429:
+					position, tokenIndex = position429, tokenIndex429
+				}
+			l430:
+				add(ruleSymbolRef, position422)
+			}
+			return true
+		l421:
+			position, tokenIndex = position421, tokenIndex421
+			return false
+		},
+		/* 34 BaseIndexScale <- <('(' RegisterOrConstant? WS? (',' WS? RegisterOrConstant WS? (',' [0-9]+)?)? ')')> */
+		func() bool {
+			position431, tokenIndex431 := position, tokenIndex
+			{
+				position432 := position
+				if buffer[position] != rune('(') {
+					goto l431
+				}
+				position++
+				{
+					position433, tokenIndex433 := position, tokenIndex
+					if !_rules[ruleRegisterOrConstant]() {
+						goto l433
+					}
+					goto l434
+				l433:
+					position, tokenIndex = position433, tokenIndex433
+				}
+			l434:
+				{
+					position435, tokenIndex435 := position, tokenIndex
+					if !_rules[ruleWS]() {
+						goto l435
+					}
+					goto l436
+				l435:
+					position, tokenIndex = position435, tokenIndex435
+				}
+			l436:
+				{
+					position437, tokenIndex437 := position, tokenIndex
+					if buffer[position] != rune(',') {
+						goto l437
+					}
+					position++
+					{
+						position439, tokenIndex439 := position, tokenIndex
+						if !_rules[ruleWS]() {
+							goto l439
+						}
+						goto l440
+					l439:
+						position, tokenIndex = position439, tokenIndex439
+					}
+				l440:
+					if !_rules[ruleRegisterOrConstant]() {
+						goto l437
+					}
+					{
+						position441, tokenIndex441 := position, tokenIndex
+						if !_rules[ruleWS]() {
+							goto l441
+						}
+						goto l442
+					l441:
+						position, tokenIndex = position441, tokenIndex441
+					}
+				l442:
+					{
+						position443, tokenIndex443 := position, tokenIndex
+						if buffer[position] != rune(',') {
+							goto l443
+						}
+						position++
+						if c := buffer[position]; c < rune('0') || c > rune('9') {
+							goto l443
+						}
+						position++
+					l445:
+						{
+							position446, tokenIndex446 := position, tokenIndex
+							if c := buffer[position]; c < rune('0') || c > rune('9') {
+								goto l446
+							}
+							position++
+							goto l445
+						l446:
+							position, tokenIndex = position446, tokenIndex446
+						}
+						goto l444
+					l443:
+						position, tokenIndex = position443, tokenIndex443
+					}
+				l444:
+					goto l438
+				l437:
+					position, tokenIndex = position437, tokenIndex437
+				}
+			l438:
+				if buffer[position] != rune(')') {
+					goto l431
+				}
+				position++
+				add(ruleBaseIndexScale, position432)
+			}
+			return true
+		l431:
+			position, tokenIndex = position431, tokenIndex431
+			return false
+		},
+		/* 35 Operator <- <('+' / '-')> */
+		func() bool {
+			position447, tokenIndex447 := position, tokenIndex
+			{
+				position448 := position
+				{
+					position449, tokenIndex449 := position, tokenIndex
+					if buffer[position] != rune('+') {
+						goto l450
+					}
+					position++
+					goto l449
+				l450:
+					position, tokenIndex = position449, tokenIndex449
+					if buffer[position] != rune('-') {
+						goto l447
+					}
+					position++
+				}
+			l449:
+				add(ruleOperator, position448)
+			}
+			return true
+		l447:
+			position, tokenIndex = position447, tokenIndex447
+			return false
+		},
+		/* 36 Offset <- <('+'? '-'? (('0' ('b' / 'B') ('0' / '1')+) / ('0' ('x' / 'X') ([0-9] / [0-9] / ([a-f] / [A-F]))+) / [0-9]+))> */
+		func() bool {
+			position451, tokenIndex451 := position, tokenIndex
+			{
+				position452 := position
+				{
+					position453, tokenIndex453 := position, tokenIndex
+					if buffer[position] != rune('+') {
+						goto l453
+					}
+					position++
+					goto l454
+				l453:
+					position, tokenIndex = position453, tokenIndex453
+				}
+			l454:
+				{
+					position455, tokenIndex455 := position, tokenIndex
+					if buffer[position] != rune('-') {
+						goto l455
+					}
+					position++
+					goto l456
+				l455:
+					position, tokenIndex = position455, tokenIndex455
+				}
+			l456:
+				{
+					position457, tokenIndex457 := position, tokenIndex
+					if buffer[position] != rune('0') {
+						goto l458
+					}
+					position++
+					{
+						position459, tokenIndex459 := position, tokenIndex
+						if buffer[position] != rune('b') {
+							goto l460
+						}
+						position++
+						goto l459
+					l460:
+						position, tokenIndex = position459, tokenIndex459
+						if buffer[position] != rune('B') {
+							goto l458
+						}
+						position++
+					}
+				l459:
+					{
+						position463, tokenIndex463 := position, tokenIndex
+						if buffer[position] != rune('0') {
+							goto l464
+						}
+						position++
+						goto l463
+					l464:
+						position, tokenIndex = position463, tokenIndex463
+						if buffer[position] != rune('1') {
+							goto l458
+						}
+						position++
+					}
+				l463:
+				l461:
+					{
+						position462, tokenIndex462 := position, tokenIndex
+						{
+							position465, tokenIndex465 := position, tokenIndex
+							if buffer[position] != rune('0') {
+								goto l466
+							}
+							position++
+							goto l465
+						l466:
+							position, tokenIndex = position465, tokenIndex465
+							if buffer[position] != rune('1') {
+								goto l462
+							}
+							position++
+						}
+					l465:
+						goto l461
+					l462:
+						position, tokenIndex = position462, tokenIndex462
+					}
+					goto l457
+				l458:
+					position, tokenIndex = position457, tokenIndex457
+					if buffer[position] != rune('0') {
+						goto l467
+					}
+					position++
+					{
+						position468, tokenIndex468 := position, tokenIndex
+						if buffer[position] != rune('x') {
+							goto l469
+						}
+						position++
+						goto l468
+					l469:
+						position, tokenIndex = position468, tokenIndex468
+						if buffer[position] != rune('X') {
+							goto l467
+						}
+						position++
+					}
+				l468:
+					{
+						position472, tokenIndex472 := position, tokenIndex
+						if c := buffer[position]; c < rune('0') || c > rune('9') {
+							goto l473
+						}
+						position++
+						goto l472
+					l473:
+						position, tokenIndex = position472, tokenIndex472
+						if c := buffer[position]; c < rune('0') || c > rune('9') {
+							goto l474
+						}
+						position++
+						goto l472
+					l474:
+						position, tokenIndex = position472, tokenIndex472
+						{
+							position475, tokenIndex475 := position, tokenIndex
+							if c := buffer[position]; c < rune('a') || c > rune('f') {
+								goto l476
+							}
+							position++
+							goto l475
+						l476:
+							position, tokenIndex = position475, tokenIndex475
+							if c := buffer[position]; c < rune('A') || c > rune('F') {
+								goto l467
+							}
+							position++
+						}
+					l475:
+					}
+				l472:
+				l470:
+					{
+						position471, tokenIndex471 := position, tokenIndex
+						{
+							position477, tokenIndex477 := position, tokenIndex
+							if c := buffer[position]; c < rune('0') || c > rune('9') {
+								goto l478
+							}
+							position++
+							goto l477
+						l478:
+							position, tokenIndex = position477, tokenIndex477
+							if c := buffer[position]; c < rune('0') || c > rune('9') {
+								goto l479
+							}
+							position++
+							goto l477
+						l479:
+							position, tokenIndex = position477, tokenIndex477
+							{
+								position480, tokenIndex480 := position, tokenIndex
+								if c := buffer[position]; c < rune('a') || c > rune('f') {
+									goto l481
+								}
+								position++
+								goto l480
+							l481:
+								position, tokenIndex = position480, tokenIndex480
+								if c := buffer[position]; c < rune('A') || c > rune('F') {
+									goto l471
+								}
+								position++
+							}
+						l480:
+						}
+					l477:
+						goto l470
+					l471:
+						position, tokenIndex = position471, tokenIndex471
+					}
+					goto l457
+				l467:
+					position, tokenIndex = position457, tokenIndex457
+					if c := buffer[position]; c < rune('0') || c > rune('9') {
+						goto l451
+					}
+					position++
+				l482:
+					{
+						position483, tokenIndex483 := position, tokenIndex
+						if c := buffer[position]; c < rune('0') || c > rune('9') {
+							goto l483
+						}
+						position++
+						goto l482
+					l483:
+						position, tokenIndex = position483, tokenIndex483
+					}
+				}
+			l457:
+				add(ruleOffset, position452)
+			}
+			return true
+		l451:
+			position, tokenIndex = position451, tokenIndex451
+			return false
+		},
+		/* 37 Section <- <([a-z] / [A-Z] / '@')+> */
+		func() bool {
+			position484, tokenIndex484 := position, tokenIndex
+			{
+				position485 := position
+				{
+					position488, tokenIndex488 := position, tokenIndex
+					if c := buffer[position]; c < rune('a') || c > rune('z') {
+						goto l489
+					}
+					position++
+					goto l488
+				l489:
+					position, tokenIndex = position488, tokenIndex488
+					if c := buffer[position]; c < rune('A') || c > rune('Z') {
+						goto l490
+					}
+					position++
+					goto l488
+				l490:
+					position, tokenIndex = position488, tokenIndex488
+					if buffer[position] != rune('@') {
+						goto l484
+					}
+					position++
+				}
+			l488:
+			l486:
+				{
+					position487, tokenIndex487 := position, tokenIndex
+					{
+						position491, tokenIndex491 := position, tokenIndex
+						if c := buffer[position]; c < rune('a') || c > rune('z') {
+							goto l492
+						}
+						position++
+						goto l491
+					l492:
+						position, tokenIndex = position491, tokenIndex491
+						if c := buffer[position]; c < rune('A') || c > rune('Z') {
+							goto l493
+						}
+						position++
+						goto l491
+					l493:
+						position, tokenIndex = position491, tokenIndex491
+						if buffer[position] != rune('@') {
+							goto l487
+						}
+						position++
+					}
+				l491:
+					goto l486
+				l487:
+					position, tokenIndex = position487, tokenIndex487
+				}
+				add(ruleSection, position485)
+			}
+			return true
+		l484:
+			position, tokenIndex = position484, tokenIndex484
+			return false
+		},
+		/* 38 SegmentRegister <- <('%' ([c-g] / 's') ('s' ':'))> */
+		func() bool {
+			position494, tokenIndex494 := position, tokenIndex
+			{
+				position495 := position
+				if buffer[position] != rune('%') {
+					goto l494
+				}
+				position++
+				{
+					position496, tokenIndex496 := position, tokenIndex
+					if c := buffer[position]; c < rune('c') || c > rune('g') {
+						goto l497
+					}
+					position++
+					goto l496
+				l497:
+					position, tokenIndex = position496, tokenIndex496
+					if buffer[position] != rune('s') {
+						goto l494
+					}
+					position++
+				}
+			l496:
+				if buffer[position] != rune('s') {
+					goto l494
+				}
+				position++
+				if buffer[position] != rune(':') {
+					goto l494
+				}
+				position++
+				add(ruleSegmentRegister, position495)
+			}
+			return true
+		l494:
+			position, tokenIndex = position494, tokenIndex494
+			return false
+		},
+	}
+	p.rules = _rules
+}
diff --git a/util/fipstools/delocate_test.go b/util/fipstools/delocate_test.go
new file mode 100644
index 0000000..277d188
--- /dev/null
+++ b/util/fipstools/delocate_test.go
@@ -0,0 +1,84 @@
+// Copyright (c) 2017, Google Inc.
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+package main
+
+import (
+	"bytes"
+	"flag"
+	"io/ioutil"
+	"path/filepath"
+	"testing"
+)
+
+var (
+	testDataDir = flag.String("testdata", "testdata", "The path to the test data directory.")
+	update      = flag.Bool("update", false, "If true, update output files rather than compare them.")
+)
+
+type delocateTest struct {
+	name string
+	in   []string
+	out  string
+}
+
+func (test *delocateTest) Path(file string) string {
+	return filepath.Join(*testDataDir, test.name, file)
+}
+
+var delocateTests = []delocateTest{
+	{"x86_64-Basic", []string{"in.s"}, "out.s"},
+	{"x86_64-Sections", []string{"in.s"}, "out.s"},
+	{"x86_64-LabelRewrite", []string{"in1.s", "in2.s"}, "out.s"},
+	{"x86_64-GOTRewrite", []string{"in.s"}, "out.s"},
+	{"x86_64-BSS", []string{"in.s"}, "out.s"},
+	{"ppc64le-Sample", []string{"in.s"}, "out.s"},
+	{"ppc64le-Sample2", []string{"in.s"}, "out.s"},
+	{"ppc64le-TOCWithOffset", []string{"in.s"}, "out.s"},
+}
+
+func TestDelocate(t *testing.T) {
+	for _, test := range delocateTests {
+		t.Run(test.name, func(t *testing.T) {
+			var inputs []inputFile
+			for i, in := range test.in {
+				inputs = append(inputs, inputFile{
+					index: i,
+					path:  test.Path(in),
+				})
+			}
+
+			if err := parseInputs(inputs); err != nil {
+				t.Fatalf("parseInputs failed: %s", err)
+			}
+
+			var buf bytes.Buffer
+			if err := transform(&buf, inputs); err != nil {
+				t.Fatalf("transform failed: %s", err)
+			}
+
+			if *update {
+				ioutil.WriteFile(test.Path(test.out), buf.Bytes(), 0666)
+			} else {
+				expected, err := ioutil.ReadFile(test.Path(test.out))
+				if err != nil {
+					t.Fatalf("could not read %q: %s", test.Path(test.out), err)
+				}
+				if !bytes.Equal(buf.Bytes(), expected) {
+					t.Errorf("delocated output differed. Wanted:\n%s\nGot:\n%s\n", expected, buf.Bytes())
+				}
+			}
+		})
+	}
+}
diff --git a/util/fipstools/testdata/ppc64le-Sample/in.s b/util/fipstools/testdata/ppc64le-Sample/in.s
new file mode 100644
index 0000000..6e7422a
--- /dev/null
+++ b/util/fipstools/testdata/ppc64le-Sample/in.s
@@ -0,0 +1,161 @@
+	.file	"foo.c"
+	.abiversion 2
+	.section	".toc","aw"
+	.section	".text"
+	.section	.rodata
+	.align 3
+	.type	kString, @object
+	.size	kString, 12
+kString:
+	.string	"hello world"
+	.globl kExportedString
+	.align 3
+	.type	kExportedString, @object
+	.size	kExportedString, 26
+kExportedString:
+	.string	"hello world, more visibly"
+	.align 2
+	.type	kGiantArray, @object
+	.size	kGiantArray, 400000
+kGiantArray:
+	.long	1
+	.long	0
+	.zero	399992
+	.lcomm	bss,20,4
+	.type	bss, @object
+	.align 3
+.LC1:
+	.string	"kString is %p\n"
+	.align 3
+.LC2:
+	.string	"kExportedString is %p\n"
+	.align 3
+.LC4:
+	.string	"function is %p\n"
+	.align 3
+.LC5:
+	.string	"exported_function is %p\n"
+	.align 3
+.LC7:
+	.string	"&kString[5] is %p\n"
+	.align 3
+.LC9:
+	.string	"&kGiantArray[0x12345] is %p\n"
+	.section	".toc","aw"
+.LC0:
+	.quad	stderr
+.LC3:
+	.quad	kExportedString
+.LC6:
+	.quad	exported_function
+.LC8:
+	.quad	kString+5
+.LC10:
+	.quad	kGiantArray+298260
+	.section	".text"
+	.align 2
+	.type	function, @function
+function:
+0:	addis 2,12,.TOC.-0b@ha
+	addi 2,2,.TOC.-0b@l
+	.localentry	function,.-function
+	mflr 0
+	std 0,16(1)
+	std 31,-8(1)
+	stdu 1,-112(1)
+	mr 31,1
+	addis 10,2,.LC0@toc@ha
+	ld 9,.LC0@toc@l(10)
+	ld 9,0(9)
+	mr 3,9
+	addis 4,2,.LC1@toc@ha
+	addi 4,4,.LC1@toc@l
+	addis 5,2,kString@toc@ha
+	addi 5,5,kString@toc@l
+	bl fprintf
+	nop
+	addis 10,2,.LC0@toc@ha
+	ld 9,.LC0@toc@l(10)
+	ld 9,0(9)
+	mr 3,9
+	addis 4,2,.LC2@toc@ha
+	addi 4,4,.LC2@toc@l
+	addis 9,2,.LC3@toc@ha
+	ld 5,.LC3@toc@l(9)
+	bl fprintf
+	nop
+	addis 10,2,.LC0@toc@ha
+	ld 9,.LC0@toc@l(10)
+	ld 9,0(9)
+	mr 3,9
+	addis 4,2,.LC4@toc@ha
+	addi 4,4,.LC4@toc@l
+	addis 5,2,function@toc@ha
+	addi 5,5,function@toc@l
+	bl fprintf
+	nop
+	addis 10,2,.LC0@toc@ha
+	ld 9,.LC0@toc@l(10)
+	ld 9,0(9)
+	mr 3,9
+	addis 4,2,.LC5@toc@ha
+	addi 4,4,.LC5@toc@l
+	addis 9,2,.LC6@toc@ha
+	ld 5,.LC6@toc@l(9)
+	bl fprintf
+	nop
+	addis 10,2,.LC0@toc@ha
+	ld 9,.LC0@toc@l(10)
+	ld 9,0(9)
+	mr 3,9
+	addis 4,2,.LC7@toc@ha
+	addi 4,4,.LC7@toc@l
+	addis 9,2,.LC8@toc@ha
+	ld 5,.LC8@toc@l(9)
+	bl fprintf
+	nop
+	addis 10,2,.LC0@toc@ha
+	ld 9,.LC0@toc@l(10)
+	ld 9,0(9)
+	mr 3,9
+	addis 4,2,.LC9@toc@ha
+	addi 4,4,.LC9@toc@l
+	addis 9,2,.LC10@toc@ha
+	ld 5,.LC10@toc@l(9)
+	bl fprintf
+	nop
+	bl exported_function
+	nop
+	mr 3,9
+	addi 1,31,112
+	ld 0,16(1)
+	mtlr 0
+	ld 31,-8(1)
+	blr
+	.long 0
+	.byte 0,0,0,1,128,1,0,1
+	.size	function,.-function
+	.align 2
+	.globl exported_function
+	.type	exported_function, @function
+exported_function:
+0:	addis 2,12,.TOC.-0b@ha
+	addi 2,2,.TOC.-0b@l
+	.localentry	exported_function,.-exported_function
+	mflr 0
+	std 0,16(1)
+	std 31,-8(1)
+	stdu 1,-48(1)
+	mr 31,1
+	bl function
+	mr 3,9
+	addi 1,31,48
+	ld 0,16(1)
+	mtlr 0
+	ld 31,-8(1)
+	blr
+	.long 0
+	.byte 0,0,0,1,128,1,0,1
+	.size	exported_function,.-exported_function
+	.ident	"GCC: (Ubuntu 4.9.2-10ubuntu13) 4.9.2"
+	.section	.note.GNU-stack,"",@progbits
diff --git a/util/fipstools/testdata/ppc64le-Sample/out.s b/util/fipstools/testdata/ppc64le-Sample/out.s
new file mode 100644
index 0000000..929d80f
--- /dev/null
+++ b/util/fipstools/testdata/ppc64le-Sample/out.s
@@ -0,0 +1,575 @@
+.text
+BORINGSSL_bcm_text_start:
+	.file	"foo.c"
+	.abiversion 2
+	.section	".toc","aw"
+# WAS .section	".text"
+.text
+# WAS .section	.rodata
+.text
+	.align 3
+	.type	kString, @object
+	.size	kString, 12
+.LkString_local_target:
+kString:
+	.string	"hello world"
+	.globl kExportedString
+	.align 3
+	.type	kExportedString, @object
+	.size	kExportedString, 26
+.LkExportedString_local_target:
+kExportedString:
+	.string	"hello world, more visibly"
+	.align 2
+	.type	kGiantArray, @object
+	.size	kGiantArray, 400000
+.LkGiantArray_local_target:
+kGiantArray:
+	.long	1
+	.long	0
+	.zero	399992
+	.lcomm	bss,20,4
+	.type	bss, @object
+	.align 3
+.LC1:
+
+	.string	"kString is %p\n"
+	.align 3
+.LC2:
+
+	.string	"kExportedString is %p\n"
+	.align 3
+.LC4:
+
+	.string	"function is %p\n"
+	.align 3
+.LC5:
+
+	.string	"exported_function is %p\n"
+	.align 3
+.LC7:
+
+	.string	"&kString[5] is %p\n"
+	.align 3
+.LC9:
+
+	.string	"&kGiantArray[0x12345] is %p\n"
+	.section	".toc","aw"
+.LC0:
+
+	.quad	stderr
+.LC3:
+
+	.quad	kExportedString
+.LC6:
+
+	.quad	exported_function
+.LC8:
+
+	.quad	kString+5
+.LC10:
+
+	.quad	kGiantArray+298260
+# WAS .section	".text"
+.text
+	.align 2
+	.type	function, @function
+.Lfunction_local_target:
+function:
+0:
+999:
+	addis 2, 12, .LBORINGSSL_external_toc-999b@ha
+	addi 2, 2, .LBORINGSSL_external_toc-999b@l
+	ld 12, 0(2)
+	add 2, 2, 12
+# WAS addi 2,2,.TOC.-0b@l
+	.localentry	function,.-function
+.Lfunction_local_entry:
+	mflr 0
+	std 0,16(1)
+	std 31,-8(1)
+	stdu 1,-112(1)
+	mr 31,1
+# WAS addis 10,2,.LC0@toc@ha
+# WAS ld 9,.LC0@toc@l(10)
+	addi 1, 1, -288
+	mflr 9
+	std 9, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC0
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 9, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 9, 0(9)
+	ld 9,0(9)
+	mr 3,9
+# WAS addis 4,2,.LC1@toc@ha
+# WAS addi 4,4,.LC1@toc@l
+	addi 1, 1, -288
+	mflr 4
+	std 4, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC1
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 4, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+# WAS addis 5,2,kString@toc@ha
+# WAS addi 5,5,kString@toc@l
+	addi 1, 1, -288
+	mflr 5
+	std 5, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LkString_local_target
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 5, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+# WAS bl fprintf
+	bl	bcm_redirector_fprintf
+	nop
+# WAS addis 10,2,.LC0@toc@ha
+# WAS ld 9,.LC0@toc@l(10)
+	addi 1, 1, -288
+	mflr 9
+	std 9, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC0
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 9, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 9, 0(9)
+	ld 9,0(9)
+	mr 3,9
+# WAS addis 4,2,.LC2@toc@ha
+# WAS addi 4,4,.LC2@toc@l
+	addi 1, 1, -288
+	mflr 4
+	std 4, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC2
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 4, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+# WAS addis 9,2,.LC3@toc@ha
+# WAS ld 5,.LC3@toc@l(9)
+	addi 1, 1, -288
+	mflr 5
+	std 5, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC3
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 5, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 5, 0(5)
+# WAS bl fprintf
+	bl	bcm_redirector_fprintf
+	nop
+# WAS addis 10,2,.LC0@toc@ha
+# WAS ld 9,.LC0@toc@l(10)
+	addi 1, 1, -288
+	mflr 9
+	std 9, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC0
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 9, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 9, 0(9)
+	ld 9,0(9)
+	mr 3,9
+# WAS addis 4,2,.LC4@toc@ha
+# WAS addi 4,4,.LC4@toc@l
+	addi 1, 1, -288
+	mflr 4
+	std 4, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC4
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 4, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+# WAS addis 5,2,function@toc@ha
+# WAS addi 5,5,function@toc@l
+	addi 1, 1, -288
+	mflr 5
+	std 5, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_Lfunction_local_target
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 5, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+# WAS bl fprintf
+	bl	bcm_redirector_fprintf
+	nop
+# WAS addis 10,2,.LC0@toc@ha
+# WAS ld 9,.LC0@toc@l(10)
+	addi 1, 1, -288
+	mflr 9
+	std 9, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC0
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 9, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 9, 0(9)
+	ld 9,0(9)
+	mr 3,9
+# WAS addis 4,2,.LC5@toc@ha
+# WAS addi 4,4,.LC5@toc@l
+	addi 1, 1, -288
+	mflr 4
+	std 4, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC5
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 4, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+# WAS addis 9,2,.LC6@toc@ha
+# WAS ld 5,.LC6@toc@l(9)
+	addi 1, 1, -288
+	mflr 5
+	std 5, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC6
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 5, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 5, 0(5)
+# WAS bl fprintf
+	bl	bcm_redirector_fprintf
+	nop
+# WAS addis 10,2,.LC0@toc@ha
+# WAS ld 9,.LC0@toc@l(10)
+	addi 1, 1, -288
+	mflr 9
+	std 9, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC0
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 9, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 9, 0(9)
+	ld 9,0(9)
+	mr 3,9
+# WAS addis 4,2,.LC7@toc@ha
+# WAS addi 4,4,.LC7@toc@l
+	addi 1, 1, -288
+	mflr 4
+	std 4, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC7
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 4, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+# WAS addis 9,2,.LC8@toc@ha
+# WAS ld 5,.LC8@toc@l(9)
+	addi 1, 1, -288
+	mflr 5
+	std 5, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC8
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 5, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 5, 0(5)
+# WAS bl fprintf
+	bl	bcm_redirector_fprintf
+	nop
+# WAS addis 10,2,.LC0@toc@ha
+# WAS ld 9,.LC0@toc@l(10)
+	addi 1, 1, -288
+	mflr 9
+	std 9, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC0
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 9, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 9, 0(9)
+	ld 9,0(9)
+	mr 3,9
+# WAS addis 4,2,.LC9@toc@ha
+# WAS addi 4,4,.LC9@toc@l
+	addi 1, 1, -288
+	mflr 4
+	std 4, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC9
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 4, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+# WAS addis 9,2,.LC10@toc@ha
+# WAS ld 5,.LC10@toc@l(9)
+	addi 1, 1, -288
+	mflr 5
+	std 5, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC10
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 5, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 5, 0(5)
+# WAS bl fprintf
+	bl	bcm_redirector_fprintf
+	nop
+# WAS bl exported_function
+	bl	.Lexported_function_local_entry
+	nop
+	mr 3,9
+	addi 1,31,112
+	ld 0,16(1)
+	mtlr 0
+	ld 31,-8(1)
+	blr
+	.long 0
+	.byte 0,0,0,1,128,1,0,1
+	.size	function,.-function
+	.align 2
+	.globl exported_function
+	.type	exported_function, @function
+.Lexported_function_local_target:
+exported_function:
+0:
+999:
+	addis 2, 12, .LBORINGSSL_external_toc-999b@ha
+	addi 2, 2, .LBORINGSSL_external_toc-999b@l
+	ld 12, 0(2)
+	add 2, 2, 12
+# WAS addi 2,2,.TOC.-0b@l
+	.localentry	exported_function,.-exported_function
+.Lexported_function_local_entry:
+	mflr 0
+	std 0,16(1)
+	std 31,-8(1)
+	stdu 1,-48(1)
+	mr 31,1
+# WAS bl function
+	bl	.Lfunction_local_entry
+	mr 3,9
+	addi 1,31,48
+	ld 0,16(1)
+	mtlr 0
+	ld 31,-8(1)
+	blr
+	.long 0
+	.byte 0,0,0,1,128,1,0,1
+	.size	exported_function,.-exported_function
+	.ident	"GCC: (Ubuntu 4.9.2-10ubuntu13) 4.9.2"
+	.section	.note.GNU-stack,"",@progbits
+.text
+BORINGSSL_bcm_text_end:
+.type bcm_redirector_fprintf, @function
+bcm_redirector_fprintf:
+	mflr 0
+	std 0,16(1)
+	stdu 1,-32(1)
+	bl	fprintf
+	nop
+	addi 1,1,32
+	ld 0,16(1)
+	mtlr 0
+	blr
+.type bss_bss_get, @function
+bss_bss_get:
+	addis 3, 2, bss@toc@ha
+	addi 3, 3, bss@toc@l
+	blr
+.type bcm_loadtoc__dot_LC0, @function
+bcm_loadtoc__dot_LC0:
+.Lbcm_loadtoc__dot_LC0:
+	addis 3, 2, .LC0@toc@ha
+	addi 3, 3, .LC0@toc@l
+	blr
+.type bcm_loadtoc__dot_LC1, @function
+bcm_loadtoc__dot_LC1:
+.Lbcm_loadtoc__dot_LC1:
+	addis 3, 2, .LC1@toc@ha
+	addi 3, 3, .LC1@toc@l
+	blr
+.type bcm_loadtoc__dot_LC10, @function
+bcm_loadtoc__dot_LC10:
+.Lbcm_loadtoc__dot_LC10:
+	addis 3, 2, .LC10@toc@ha
+	addi 3, 3, .LC10@toc@l
+	blr
+.type bcm_loadtoc__dot_LC2, @function
+bcm_loadtoc__dot_LC2:
+.Lbcm_loadtoc__dot_LC2:
+	addis 3, 2, .LC2@toc@ha
+	addi 3, 3, .LC2@toc@l
+	blr
+.type bcm_loadtoc__dot_LC3, @function
+bcm_loadtoc__dot_LC3:
+.Lbcm_loadtoc__dot_LC3:
+	addis 3, 2, .LC3@toc@ha
+	addi 3, 3, .LC3@toc@l
+	blr
+.type bcm_loadtoc__dot_LC4, @function
+bcm_loadtoc__dot_LC4:
+.Lbcm_loadtoc__dot_LC4:
+	addis 3, 2, .LC4@toc@ha
+	addi 3, 3, .LC4@toc@l
+	blr
+.type bcm_loadtoc__dot_LC5, @function
+bcm_loadtoc__dot_LC5:
+.Lbcm_loadtoc__dot_LC5:
+	addis 3, 2, .LC5@toc@ha
+	addi 3, 3, .LC5@toc@l
+	blr
+.type bcm_loadtoc__dot_LC6, @function
+bcm_loadtoc__dot_LC6:
+.Lbcm_loadtoc__dot_LC6:
+	addis 3, 2, .LC6@toc@ha
+	addi 3, 3, .LC6@toc@l
+	blr
+.type bcm_loadtoc__dot_LC7, @function
+bcm_loadtoc__dot_LC7:
+.Lbcm_loadtoc__dot_LC7:
+	addis 3, 2, .LC7@toc@ha
+	addi 3, 3, .LC7@toc@l
+	blr
+.type bcm_loadtoc__dot_LC8, @function
+bcm_loadtoc__dot_LC8:
+.Lbcm_loadtoc__dot_LC8:
+	addis 3, 2, .LC8@toc@ha
+	addi 3, 3, .LC8@toc@l
+	blr
+.type bcm_loadtoc__dot_LC9, @function
+bcm_loadtoc__dot_LC9:
+.Lbcm_loadtoc__dot_LC9:
+	addis 3, 2, .LC9@toc@ha
+	addi 3, 3, .LC9@toc@l
+	blr
+.type bcm_loadtoc__dot_Lfunction_local_target, @function
+bcm_loadtoc__dot_Lfunction_local_target:
+.Lbcm_loadtoc__dot_Lfunction_local_target:
+	addis 3, 2, .Lfunction_local_target@toc@ha
+	addi 3, 3, .Lfunction_local_target@toc@l
+	blr
+.type bcm_loadtoc__dot_LkString_local_target, @function
+bcm_loadtoc__dot_LkString_local_target:
+.Lbcm_loadtoc__dot_LkString_local_target:
+	addis 3, 2, .LkString_local_target@toc@ha
+	addi 3, 3, .LkString_local_target@toc@l
+	blr
+.LBORINGSSL_external_toc:
+.quad .TOC.-.LBORINGSSL_external_toc
+.type BORINGSSL_bcm_text_hash, @object
+.size BORINGSSL_bcm_text_hash, 64
+BORINGSSL_bcm_text_hash:
+.byte 0xae
+.byte 0x2c
+.byte 0xea
+.byte 0x2a
+.byte 0xbd
+.byte 0xa6
+.byte 0xf3
+.byte 0xec
+.byte 0x97
+.byte 0x7f
+.byte 0x9b
+.byte 0xf6
+.byte 0x94
+.byte 0x9a
+.byte 0xfc
+.byte 0x83
+.byte 0x68
+.byte 0x27
+.byte 0xcb
+.byte 0xa0
+.byte 0xa0
+.byte 0x9f
+.byte 0x6b
+.byte 0x6f
+.byte 0xde
+.byte 0x52
+.byte 0xcd
+.byte 0xe2
+.byte 0xcd
+.byte 0xff
+.byte 0x31
+.byte 0x80
+.byte 0xa2
+.byte 0xd4
+.byte 0xc3
+.byte 0x66
+.byte 0xf
+.byte 0xc2
+.byte 0x6a
+.byte 0x7b
+.byte 0xf4
+.byte 0xbe
+.byte 0x39
+.byte 0xa2
+.byte 0xd7
+.byte 0x25
+.byte 0xdb
+.byte 0x21
+.byte 0x98
+.byte 0xe9
+.byte 0xd5
+.byte 0x53
+.byte 0xbf
+.byte 0x5c
+.byte 0x32
+.byte 0x6
+.byte 0x83
+.byte 0x34
+.byte 0xc
+.byte 0x65
+.byte 0x89
+.byte 0x52
+.byte 0xbd
+.byte 0x1f
diff --git a/util/fipstools/testdata/ppc64le-Sample2/in.s b/util/fipstools/testdata/ppc64le-Sample2/in.s
new file mode 100644
index 0000000..eb85626
--- /dev/null
+++ b/util/fipstools/testdata/ppc64le-Sample2/in.s
@@ -0,0 +1,226 @@
+	.file	"foo.c"
+	.abiversion 2
+	.section	".toc","aw"
+	.section	".text"
+	.section	".toc","aw"
+.LC0:
+	.quad	stderr
+.LC3:
+	.quad	kExportedString
+.LC6:
+	.quad	exported_function
+	.section	".text"
+	.align 2
+	.p2align 4,,15
+	.globl exported_function
+	.type	exported_function, @function
+exported_function:
+0:	addis 2,12,.TOC.-0b@ha
+	addi 2,2,.TOC.-0b@l
+	.localentry	exported_function,.-exported_function
+	mflr 0
+	std 19,-104(1)
+	std 20,-96(1)
+	std 21,-88(1)
+	std 22,-80(1)
+	addis 21,2,.LC1@toc@ha
+	addis 22,2,.LC2@toc@ha
+	std 23,-72(1)
+	std 24,-64(1)
+	addis 23,2,.LC4@toc@ha
+	addis 24,2,function@toc@ha
+	std 25,-56(1)
+	std 26,-48(1)
+	addis 25,2,.LC5@toc@ha
+	addis 26,2,.LC7@toc@ha
+	std 27,-40(1)
+	std 28,-32(1)
+	addis 28,2,.LC8@toc@ha
+	addi 21,21,.LC1@toc@l
+	std 29,-24(1)
+	std 30,-16(1)
+	addis 29,2,.LANCHOR0@toc@ha
+	addi 22,22,.LC2@toc@l
+	std 31,-8(1)
+	std 0,16(1)
+	addi 29,29,.LANCHOR0@toc@l
+	addi 23,23,.LC4@toc@l
+	stdu 1,-208(1)
+	addis 31,2,.LC0@toc@ha		# gpr load fusion, type long
+	ld 31,.LC0@toc@l(31)
+	addis 19,2,.LC3@toc@ha		# gpr load fusion, type long
+	ld 19,.LC3@toc@l(19)
+	addis 30,29,0x5
+	addi 24,24,function@toc@l
+	addis 20,2,.LC6@toc@ha		# gpr load fusion, type long
+	ld 20,.LC6@toc@l(20)
+	addi 25,25,.LC5@toc@l
+	addi 26,26,.LC7@toc@l
+	addi 27,29,5
+	addi 28,28,.LC8@toc@l
+	addi 30,30,-29404
+	.p2align 4,,15
+.L2:
+	ld 3,0(31)
+	mr 5,21
+	mr 6,29
+	li 4,1
+	bl __fprintf_chk
+	nop
+	ld 3,0(31)
+	mr 5,22
+	mr 6,19
+	li 4,1
+	bl __fprintf_chk
+	nop
+	ld 3,0(31)
+	mr 5,23
+	mr 6,24
+	li 4,1
+	bl __fprintf_chk
+	nop
+	ld 3,0(31)
+	mr 5,25
+	mr 6,20
+	li 4,1
+	bl __fprintf_chk
+	nop
+	ld 3,0(31)
+	mr 5,26
+	mr 6,27
+	li 4,1
+	bl __fprintf_chk
+	nop
+	ld 3,0(31)
+	li 4,1
+	mr 5,28
+	mr 6,30
+	bl __fprintf_chk
+	nop
+	b .L2
+	.long 0
+	.byte 0,0,0,1,128,13,0,0
+	.size	exported_function,.-exported_function
+	.section	".toc","aw"
+	.set .LC11,.LC0
+	.set .LC12,.LC3
+	.set .LC13,.LC6
+	.section	".text"
+	.align 2
+	.p2align 4,,15
+	.type	function, @function
+function:
+0:	addis 2,12,.TOC.-0b@ha
+	addi 2,2,.TOC.-0b@l
+	.localentry	function,.-function
+	mflr 0
+	std 31,-8(1)
+	addis 31,2,.LC11@toc@ha		# gpr load fusion, type long
+	ld 31,.LC11@toc@l(31)
+	addis 5,2,.LC1@toc@ha
+	std 30,-16(1)
+	addis 30,2,.LANCHOR0@toc@ha
+	addi 5,5,.LC1@toc@l
+	addi 30,30,.LANCHOR0@toc@l
+	li 4,1
+	mr 6,30
+	std 0,16(1)
+	stdu 1,-112(1)
+	ld 3,0(31)
+	bl __fprintf_chk
+	nop
+	addis 6,2,.LC12@toc@ha		# gpr load fusion, type long
+	ld 6,.LC12@toc@l(6)
+	ld 3,0(31)
+	addis 5,2,.LC2@toc@ha
+	li 4,1
+	addi 5,5,.LC2@toc@l
+	bl __fprintf_chk
+	nop
+	ld 3,0(31)
+	addis 5,2,.LC4@toc@ha
+	addis 6,2,function@toc@ha
+	addi 5,5,.LC4@toc@l
+	addi 6,6,function@toc@l
+	li 4,1
+	bl __fprintf_chk
+	nop
+	addis 6,2,.LC13@toc@ha		# gpr load fusion, type long
+	ld 6,.LC13@toc@l(6)
+	ld 3,0(31)
+	addis 5,2,.LC5@toc@ha
+	li 4,1
+	addi 5,5,.LC5@toc@l
+	bl __fprintf_chk
+	nop
+	ld 3,0(31)
+	addis 5,2,.LC7@toc@ha
+	addi 6,30,5
+	addi 5,5,.LC7@toc@l
+	li 4,1
+	bl __fprintf_chk
+	nop
+	ld 3,0(31)
+	addis 6,30,0x5
+	addis 5,2,.LC8@toc@ha
+	li 4,1
+	addi 5,5,.LC8@toc@l
+	addi 6,6,-29404
+	bl __fprintf_chk
+	nop
+	bl exported_function
+	nop
+	addi 1,1,112
+	ld 0,16(1)
+	ld 30,-16(1)
+	ld 31,-8(1)
+	mtlr 0
+	blr
+	.long 0
+	.byte 0,0,0,1,128,2,0,0
+	.size	function,.-function
+	.globl kExportedString
+	.section	.rodata
+	.align 4
+	.set	.LANCHOR0,. + 0
+	.type	kString, @object
+	.size	kString, 12
+kString:
+	.string	"hello world"
+	.zero	4
+	.type	kGiantArray, @object
+	.size	kGiantArray, 400000
+kGiantArray:
+	.long	1
+	.long	0
+	.zero	399992
+	.type	kExportedString, @object
+	.size	kExportedString, 26
+kExportedString:
+	.string	"hello world, more visibly"
+	.section	.rodata.str1.8,"aMS",@progbits,1
+	.align 3
+.LC1:
+	.string	"kString is %p\n"
+	.zero	1
+.LC2:
+	.string	"kExportedString is %p\n"
+	.zero	1
+.LC4:
+	.string	"function is %p\n"
+.LC5:
+	.string	"exported_function is %p\n"
+	.zero	7
+.LC7:
+	.string	"&kString[5] is %p\n"
+	.zero	5
+.LC8:
+	.string	"&kGiantArray[0x12345] is %p\n"
+	.section	".bss"
+	.align 2
+	.type	bss, @object
+	.size	bss, 20
+bss:
+	.zero	20
+	.ident	"GCC: (Ubuntu 4.9.2-10ubuntu13) 4.9.2"
+	.section	.note.GNU-stack,"",@progbits
diff --git a/util/fipstools/testdata/ppc64le-Sample2/out.s b/util/fipstools/testdata/ppc64le-Sample2/out.s
new file mode 100644
index 0000000..29291dc
--- /dev/null
+++ b/util/fipstools/testdata/ppc64le-Sample2/out.s
@@ -0,0 +1,694 @@
+.text
+BORINGSSL_bcm_text_start:
+	.file	"foo.c"
+	.abiversion 2
+	.section	".toc","aw"
+# WAS .section	".text"
+.text
+	.section	".toc","aw"
+.LC0:
+
+	.quad	stderr
+.LC3:
+
+	.quad	kExportedString
+.LC6:
+
+	.quad	exported_function
+# WAS .section	".text"
+.text
+	.align 2
+	.p2align 4,,15
+	.globl exported_function
+	.type	exported_function, @function
+.Lexported_function_local_target:
+exported_function:
+0:
+999:
+	addis 2, 12, .LBORINGSSL_external_toc-999b@ha
+	addi 2, 2, .LBORINGSSL_external_toc-999b@l
+	ld 12, 0(2)
+	add 2, 2, 12
+# WAS addi 2,2,.TOC.-0b@l
+	.localentry	exported_function,.-exported_function
+.Lexported_function_local_entry:
+	mflr 0
+	std 19,-104(1)
+	std 20,-96(1)
+	std 21,-88(1)
+	std 22,-80(1)
+# WAS addis 21,2,.LC1@toc@ha
+# WAS addis 22,2,.LC2@toc@ha
+	std 23,-72(1)
+	std 24,-64(1)
+# WAS addis 23,2,.LC4@toc@ha
+# WAS addis 24,2,function@toc@ha
+	std 25,-56(1)
+	std 26,-48(1)
+# WAS addis 25,2,.LC5@toc@ha
+# WAS addis 26,2,.LC7@toc@ha
+	std 27,-40(1)
+	std 28,-32(1)
+# WAS addis 28,2,.LC8@toc@ha
+# WAS addi 21,21,.LC1@toc@l
+	addi 1, 1, -288
+	mflr 21
+	std 21, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC1
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 21, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	std 29,-24(1)
+	std 30,-16(1)
+# WAS addis 29,2,.LANCHOR0@toc@ha
+# WAS addi 22,22,.LC2@toc@l
+	addi 1, 1, -288
+	mflr 22
+	std 22, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC2
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 22, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	std 31,-8(1)
+	std 0,16(1)
+# WAS addi 29,29,.LANCHOR0@toc@l
+	addi 1, 1, -288
+	mflr 29
+	std 29, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LANCHOR0
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 29, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+# WAS addi 23,23,.LC4@toc@l
+	addi 1, 1, -288
+	mflr 23
+	std 23, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC4
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 23, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	stdu 1,-208(1)
+# WAS addis 31,2,.LC0@toc@ha		# gpr load fusion, type long
+# WAS ld 31,.LC0@toc@l(31)
+	addi 1, 1, -288
+	mflr 31
+	std 31, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC0
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 31, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 31, 0(31)
+# WAS addis 19,2,.LC3@toc@ha		# gpr load fusion, type long
+# WAS ld 19,.LC3@toc@l(19)
+	addi 1, 1, -288
+	mflr 19
+	std 19, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC3
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 19, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 19, 0(19)
+	addis 30,29,0x5
+# WAS addi 24,24,function@toc@l
+	addi 1, 1, -288
+	mflr 24
+	std 24, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_Lfunction_local_target
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 24, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+# WAS addis 20,2,.LC6@toc@ha		# gpr load fusion, type long
+# WAS ld 20,.LC6@toc@l(20)
+	addi 1, 1, -288
+	mflr 20
+	std 20, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC6
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 20, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 20, 0(20)
+# WAS addi 25,25,.LC5@toc@l
+	addi 1, 1, -288
+	mflr 25
+	std 25, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC5
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 25, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+# WAS addi 26,26,.LC7@toc@l
+	addi 1, 1, -288
+	mflr 26
+	std 26, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC7
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 26, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	addi 27,29,5
+# WAS addi 28,28,.LC8@toc@l
+	addi 1, 1, -288
+	mflr 28
+	std 28, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC8
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 28, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	addi 30,30,-29404
+	.p2align 4,,15
+.L2:
+
+	ld 3,0(31)
+	mr 5,21
+	mr 6,29
+	li 4,1
+# WAS bl __fprintf_chk
+	bl	bcm_redirector___fprintf_chk
+	nop
+	ld 3,0(31)
+	mr 5,22
+	mr 6,19
+	li 4,1
+# WAS bl __fprintf_chk
+	bl	bcm_redirector___fprintf_chk
+	nop
+	ld 3,0(31)
+	mr 5,23
+	mr 6,24
+	li 4,1
+# WAS bl __fprintf_chk
+	bl	bcm_redirector___fprintf_chk
+	nop
+	ld 3,0(31)
+	mr 5,25
+	mr 6,20
+	li 4,1
+# WAS bl __fprintf_chk
+	bl	bcm_redirector___fprintf_chk
+	nop
+	ld 3,0(31)
+	mr 5,26
+	mr 6,27
+	li 4,1
+# WAS bl __fprintf_chk
+	bl	bcm_redirector___fprintf_chk
+	nop
+	ld 3,0(31)
+	li 4,1
+	mr 5,28
+	mr 6,30
+# WAS bl __fprintf_chk
+	bl	bcm_redirector___fprintf_chk
+	nop
+	b .L2
+	.long 0
+	.byte 0,0,0,1,128,13,0,0
+	.size	exported_function,.-exported_function
+	.section	".toc","aw"
+	.set .LC11,.LC0
+	.set .LC12,.LC3
+	.set .LC13,.LC6
+# WAS .section	".text"
+.text
+	.align 2
+	.p2align 4,,15
+	.type	function, @function
+.Lfunction_local_target:
+function:
+0:
+999:
+	addis 2, 12, .LBORINGSSL_external_toc-999b@ha
+	addi 2, 2, .LBORINGSSL_external_toc-999b@l
+	ld 12, 0(2)
+	add 2, 2, 12
+# WAS addi 2,2,.TOC.-0b@l
+	.localentry	function,.-function
+.Lfunction_local_entry:
+	mflr 0
+	std 31,-8(1)
+# WAS addis 31,2,.LC11@toc@ha		# gpr load fusion, type long
+# WAS ld 31,.LC11@toc@l(31)
+	addi 1, 1, -288
+	mflr 31
+	std 31, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC11
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 31, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 31, 0(31)
+# WAS addis 5,2,.LC1@toc@ha
+	std 30,-16(1)
+# WAS addis 30,2,.LANCHOR0@toc@ha
+# WAS addi 5,5,.LC1@toc@l
+	addi 1, 1, -288
+	mflr 5
+	std 5, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC1
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 5, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+# WAS addi 30,30,.LANCHOR0@toc@l
+	addi 1, 1, -288
+	mflr 30
+	std 30, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LANCHOR0
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 30, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	li 4,1
+	mr 6,30
+	std 0,16(1)
+	stdu 1,-112(1)
+	ld 3,0(31)
+# WAS bl __fprintf_chk
+	bl	bcm_redirector___fprintf_chk
+	nop
+# WAS addis 6,2,.LC12@toc@ha		# gpr load fusion, type long
+# WAS ld 6,.LC12@toc@l(6)
+	addi 1, 1, -288
+	mflr 6
+	std 6, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC12
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 6, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 6, 0(6)
+	ld 3,0(31)
+# WAS addis 5,2,.LC2@toc@ha
+	li 4,1
+# WAS addi 5,5,.LC2@toc@l
+	addi 1, 1, -288
+	mflr 5
+	std 5, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC2
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 5, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+# WAS bl __fprintf_chk
+	bl	bcm_redirector___fprintf_chk
+	nop
+	ld 3,0(31)
+# WAS addis 5,2,.LC4@toc@ha
+# WAS addis 6,2,function@toc@ha
+# WAS addi 5,5,.LC4@toc@l
+	addi 1, 1, -288
+	mflr 5
+	std 5, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC4
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 5, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+# WAS addi 6,6,function@toc@l
+	addi 1, 1, -288
+	mflr 6
+	std 6, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_Lfunction_local_target
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 6, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	li 4,1
+# WAS bl __fprintf_chk
+	bl	bcm_redirector___fprintf_chk
+	nop
+# WAS addis 6,2,.LC13@toc@ha		# gpr load fusion, type long
+# WAS ld 6,.LC13@toc@l(6)
+	addi 1, 1, -288
+	mflr 6
+	std 6, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC13
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 6, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 6, 0(6)
+	ld 3,0(31)
+# WAS addis 5,2,.LC5@toc@ha
+	li 4,1
+# WAS addi 5,5,.LC5@toc@l
+	addi 1, 1, -288
+	mflr 5
+	std 5, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC5
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 5, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+# WAS bl __fprintf_chk
+	bl	bcm_redirector___fprintf_chk
+	nop
+	ld 3,0(31)
+# WAS addis 5,2,.LC7@toc@ha
+	addi 6,30,5
+# WAS addi 5,5,.LC7@toc@l
+	addi 1, 1, -288
+	mflr 5
+	std 5, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC7
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 5, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	li 4,1
+# WAS bl __fprintf_chk
+	bl	bcm_redirector___fprintf_chk
+	nop
+	ld 3,0(31)
+	addis 6,30,0x5
+# WAS addis 5,2,.LC8@toc@ha
+	li 4,1
+# WAS addi 5,5,.LC8@toc@l
+	addi 1, 1, -288
+	mflr 5
+	std 5, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_LC8
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 5, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	addi 6,6,-29404
+# WAS bl __fprintf_chk
+	bl	bcm_redirector___fprintf_chk
+	nop
+# WAS bl exported_function
+	bl	.Lexported_function_local_entry
+	nop
+	addi 1,1,112
+	ld 0,16(1)
+	ld 30,-16(1)
+	ld 31,-8(1)
+	mtlr 0
+	blr
+	.long 0
+	.byte 0,0,0,1,128,2,0,0
+	.size	function,.-function
+	.globl kExportedString
+# WAS .section	.rodata
+.text
+	.align 4
+	.set	.LANCHOR0,. + 0
+	.type	kString, @object
+	.size	kString, 12
+.LkString_local_target:
+kString:
+	.string	"hello world"
+	.zero	4
+	.type	kGiantArray, @object
+	.size	kGiantArray, 400000
+.LkGiantArray_local_target:
+kGiantArray:
+	.long	1
+	.long	0
+	.zero	399992
+	.type	kExportedString, @object
+	.size	kExportedString, 26
+.LkExportedString_local_target:
+kExportedString:
+	.string	"hello world, more visibly"
+# WAS .section	.rodata.str1.8,"aMS",@progbits,1
+.text
+	.align 3
+.LC1:
+
+	.string	"kString is %p\n"
+	.zero	1
+.LC2:
+
+	.string	"kExportedString is %p\n"
+	.zero	1
+.LC4:
+
+	.string	"function is %p\n"
+.LC5:
+
+	.string	"exported_function is %p\n"
+	.zero	7
+.LC7:
+
+	.string	"&kString[5] is %p\n"
+	.zero	5
+.LC8:
+
+	.string	"&kGiantArray[0x12345] is %p\n"
+	.section	".bss"
+	.align 2
+	.type	bss, @object
+	.size	bss, 20
+bss:
+.Lbss_local_target:
+
+	.zero	20
+	.ident	"GCC: (Ubuntu 4.9.2-10ubuntu13) 4.9.2"
+	.section	.note.GNU-stack,"",@progbits
+.text
+BORINGSSL_bcm_text_end:
+.type bcm_redirector___fprintf_chk, @function
+bcm_redirector___fprintf_chk:
+	mflr 0
+	std 0,16(1)
+	stdu 1,-32(1)
+	bl	__fprintf_chk
+	nop
+	addi 1,1,32
+	ld 0,16(1)
+	mtlr 0
+	blr
+.type bss_bss_get, @function
+bss_bss_get:
+	addis 3, 2, .Lbss_local_target@toc@ha
+	addi 3, 3, .Lbss_local_target@toc@l
+	blr
+.type bcm_loadtoc__dot_LANCHOR0, @function
+bcm_loadtoc__dot_LANCHOR0:
+.Lbcm_loadtoc__dot_LANCHOR0:
+	addis 3, 2, .LANCHOR0@toc@ha
+	addi 3, 3, .LANCHOR0@toc@l
+	blr
+.type bcm_loadtoc__dot_LC0, @function
+bcm_loadtoc__dot_LC0:
+.Lbcm_loadtoc__dot_LC0:
+	addis 3, 2, .LC0@toc@ha
+	addi 3, 3, .LC0@toc@l
+	blr
+.type bcm_loadtoc__dot_LC1, @function
+bcm_loadtoc__dot_LC1:
+.Lbcm_loadtoc__dot_LC1:
+	addis 3, 2, .LC1@toc@ha
+	addi 3, 3, .LC1@toc@l
+	blr
+.type bcm_loadtoc__dot_LC11, @function
+bcm_loadtoc__dot_LC11:
+.Lbcm_loadtoc__dot_LC11:
+	addis 3, 2, .LC11@toc@ha
+	addi 3, 3, .LC11@toc@l
+	blr
+.type bcm_loadtoc__dot_LC12, @function
+bcm_loadtoc__dot_LC12:
+.Lbcm_loadtoc__dot_LC12:
+	addis 3, 2, .LC12@toc@ha
+	addi 3, 3, .LC12@toc@l
+	blr
+.type bcm_loadtoc__dot_LC13, @function
+bcm_loadtoc__dot_LC13:
+.Lbcm_loadtoc__dot_LC13:
+	addis 3, 2, .LC13@toc@ha
+	addi 3, 3, .LC13@toc@l
+	blr
+.type bcm_loadtoc__dot_LC2, @function
+bcm_loadtoc__dot_LC2:
+.Lbcm_loadtoc__dot_LC2:
+	addis 3, 2, .LC2@toc@ha
+	addi 3, 3, .LC2@toc@l
+	blr
+.type bcm_loadtoc__dot_LC3, @function
+bcm_loadtoc__dot_LC3:
+.Lbcm_loadtoc__dot_LC3:
+	addis 3, 2, .LC3@toc@ha
+	addi 3, 3, .LC3@toc@l
+	blr
+.type bcm_loadtoc__dot_LC4, @function
+bcm_loadtoc__dot_LC4:
+.Lbcm_loadtoc__dot_LC4:
+	addis 3, 2, .LC4@toc@ha
+	addi 3, 3, .LC4@toc@l
+	blr
+.type bcm_loadtoc__dot_LC5, @function
+bcm_loadtoc__dot_LC5:
+.Lbcm_loadtoc__dot_LC5:
+	addis 3, 2, .LC5@toc@ha
+	addi 3, 3, .LC5@toc@l
+	blr
+.type bcm_loadtoc__dot_LC6, @function
+bcm_loadtoc__dot_LC6:
+.Lbcm_loadtoc__dot_LC6:
+	addis 3, 2, .LC6@toc@ha
+	addi 3, 3, .LC6@toc@l
+	blr
+.type bcm_loadtoc__dot_LC7, @function
+bcm_loadtoc__dot_LC7:
+.Lbcm_loadtoc__dot_LC7:
+	addis 3, 2, .LC7@toc@ha
+	addi 3, 3, .LC7@toc@l
+	blr
+.type bcm_loadtoc__dot_LC8, @function
+bcm_loadtoc__dot_LC8:
+.Lbcm_loadtoc__dot_LC8:
+	addis 3, 2, .LC8@toc@ha
+	addi 3, 3, .LC8@toc@l
+	blr
+.type bcm_loadtoc__dot_Lfunction_local_target, @function
+bcm_loadtoc__dot_Lfunction_local_target:
+.Lbcm_loadtoc__dot_Lfunction_local_target:
+	addis 3, 2, .Lfunction_local_target@toc@ha
+	addi 3, 3, .Lfunction_local_target@toc@l
+	blr
+.LBORINGSSL_external_toc:
+.quad .TOC.-.LBORINGSSL_external_toc
+.type BORINGSSL_bcm_text_hash, @object
+.size BORINGSSL_bcm_text_hash, 64
+BORINGSSL_bcm_text_hash:
+.byte 0xae
+.byte 0x2c
+.byte 0xea
+.byte 0x2a
+.byte 0xbd
+.byte 0xa6
+.byte 0xf3
+.byte 0xec
+.byte 0x97
+.byte 0x7f
+.byte 0x9b
+.byte 0xf6
+.byte 0x94
+.byte 0x9a
+.byte 0xfc
+.byte 0x83
+.byte 0x68
+.byte 0x27
+.byte 0xcb
+.byte 0xa0
+.byte 0xa0
+.byte 0x9f
+.byte 0x6b
+.byte 0x6f
+.byte 0xde
+.byte 0x52
+.byte 0xcd
+.byte 0xe2
+.byte 0xcd
+.byte 0xff
+.byte 0x31
+.byte 0x80
+.byte 0xa2
+.byte 0xd4
+.byte 0xc3
+.byte 0x66
+.byte 0xf
+.byte 0xc2
+.byte 0x6a
+.byte 0x7b
+.byte 0xf4
+.byte 0xbe
+.byte 0x39
+.byte 0xa2
+.byte 0xd7
+.byte 0x25
+.byte 0xdb
+.byte 0x21
+.byte 0x98
+.byte 0xe9
+.byte 0xd5
+.byte 0x53
+.byte 0xbf
+.byte 0x5c
+.byte 0x32
+.byte 0x6
+.byte 0x83
+.byte 0x34
+.byte 0xc
+.byte 0x65
+.byte 0x89
+.byte 0x52
+.byte 0xbd
+.byte 0x1f
diff --git a/util/fipstools/testdata/ppc64le-TOCWithOffset/in.s b/util/fipstools/testdata/ppc64le-TOCWithOffset/in.s
new file mode 100644
index 0000000..abd3006
--- /dev/null
+++ b/util/fipstools/testdata/ppc64le-TOCWithOffset/in.s
@@ -0,0 +1,14 @@
+	.text
+foo:
+	# TOC references may have offsets.
+	addis 3, 2, 5+foo@toc@ha
+	addi 3, 3, 10+foo@toc@l
+
+	addis 3, 2, 15+foo@toc@ha
+	addi 3, 3, 20+foo@toc@l
+
+	addis 4, 2, foo@toc@ha
+	addi 4, 4, foo@toc@l
+
+	addis 5, 2, 5+foo@toc@ha
+	ld 5, 10+foo@toc@l(5)
diff --git a/util/fipstools/testdata/ppc64le-TOCWithOffset/out.s b/util/fipstools/testdata/ppc64le-TOCWithOffset/out.s
new file mode 100644
index 0000000..633a089
--- /dev/null
+++ b/util/fipstools/testdata/ppc64le-TOCWithOffset/out.s
@@ -0,0 +1,137 @@
+.text
+BORINGSSL_bcm_text_start:
+	.text
+.Lfoo_local_target:
+foo:
+	# TOC references may have offsets.
+# WAS addis 3, 2, 5+foo@toc@ha
+# WAS addi 3, 3, 10+foo@toc@l
+	addi 1, 1, -288
+	mflr 3
+	std 3, -8(1)
+	bl .Lbcm_loadtoc__dot_Lfoo_local_target
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 3, -24(1)
+	addi 1, 1, 288
+	addi 3, 3, +10
+
+# WAS addis 3, 2, 15+foo@toc@ha
+# WAS addi 3, 3, 20+foo@toc@l
+	addi 1, 1, -288
+	mflr 3
+	std 3, -8(1)
+	bl .Lbcm_loadtoc__dot_Lfoo_local_target
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 3, -24(1)
+	addi 1, 1, 288
+	addi 3, 3, +20
+
+# WAS addis 4, 2, foo@toc@ha
+# WAS addi 4, 4, foo@toc@l
+	addi 1, 1, -288
+	mflr 4
+	std 4, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_Lfoo_local_target
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 4, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+
+# WAS addis 5, 2, 5+foo@toc@ha
+# WAS ld 5, 10+foo@toc@l(5)
+	addi 1, 1, -288
+	mflr 5
+	std 5, -8(1)
+	std 3, -16(1)
+	bl .Lbcm_loadtoc__dot_Lfoo_local_target
+	std 3, -24(1)
+	ld 3, -8(1)
+	mtlr 3
+	ld 5, -24(1)
+	ld 3, -16(1)
+	addi 1, 1, 288
+	ld 5, +10(5)
+.text
+BORINGSSL_bcm_text_end:
+.type bcm_loadtoc__dot_Lfoo_local_target, @function
+bcm_loadtoc__dot_Lfoo_local_target:
+.Lbcm_loadtoc__dot_Lfoo_local_target:
+	addis 3, 2, .Lfoo_local_target@toc@ha
+	addi 3, 3, .Lfoo_local_target@toc@l
+	blr
+.LBORINGSSL_external_toc:
+.quad .TOC.-.LBORINGSSL_external_toc
+.type BORINGSSL_bcm_text_hash, @object
+.size BORINGSSL_bcm_text_hash, 64
+BORINGSSL_bcm_text_hash:
+.byte 0xae
+.byte 0x2c
+.byte 0xea
+.byte 0x2a
+.byte 0xbd
+.byte 0xa6
+.byte 0xf3
+.byte 0xec
+.byte 0x97
+.byte 0x7f
+.byte 0x9b
+.byte 0xf6
+.byte 0x94
+.byte 0x9a
+.byte 0xfc
+.byte 0x83
+.byte 0x68
+.byte 0x27
+.byte 0xcb
+.byte 0xa0
+.byte 0xa0
+.byte 0x9f
+.byte 0x6b
+.byte 0x6f
+.byte 0xde
+.byte 0x52
+.byte 0xcd
+.byte 0xe2
+.byte 0xcd
+.byte 0xff
+.byte 0x31
+.byte 0x80
+.byte 0xa2
+.byte 0xd4
+.byte 0xc3
+.byte 0x66
+.byte 0xf
+.byte 0xc2
+.byte 0x6a
+.byte 0x7b
+.byte 0xf4
+.byte 0xbe
+.byte 0x39
+.byte 0xa2
+.byte 0xd7
+.byte 0x25
+.byte 0xdb
+.byte 0x21
+.byte 0x98
+.byte 0xe9
+.byte 0xd5
+.byte 0x53
+.byte 0xbf
+.byte 0x5c
+.byte 0x32
+.byte 0x6
+.byte 0x83
+.byte 0x34
+.byte 0xc
+.byte 0x65
+.byte 0x89
+.byte 0x52
+.byte 0xbd
+.byte 0x1f
diff --git a/util/fipstools/testdata/x86_64-BSS/in.s b/util/fipstools/testdata/x86_64-BSS/in.s
new file mode 100644
index 0000000..2d31363
--- /dev/null
+++ b/util/fipstools/testdata/x86_64-BSS/in.s
@@ -0,0 +1,33 @@
+	.text
+	movq %rax, %rax
+	
+	# BSS declarations emit accessors.
+	.comm	aes_128_ctr_generic_storage,64,32
+	.lcomm	aes_128_ctr_generic_storage2,64,32
+	
+	# BSS symbols may also be emitted in .bss sections.
+	.section .bss,"awT",@nobits
+	.align 4
+	.globl x
+	.type   x, @object
+	.size   x, 4
+x:
+	.zero 4
+.Llocal:
+	.quad 0
+	.size .Llocal, 4
+
+	# .bss handling is terminated by a .text directive.
+	.text
+	.section .bss,"awT",@nobits
+y:
+	.quad 0
+
+	# Or a .section directive.
+	.section .rodata
+	.quad 0
+
+	# Or the end of the file.
+	.section .bss,"awT",@nobits
+z:
+	.quad 0
diff --git a/util/fipstools/testdata/x86_64-BSS/out.s b/util/fipstools/testdata/x86_64-BSS/out.s
new file mode 100644
index 0000000..77dc40d
--- /dev/null
+++ b/util/fipstools/testdata/x86_64-BSS/out.s
@@ -0,0 +1,140 @@
+.text
+BORINGSSL_bcm_text_start:
+	.text
+	movq %rax, %rax
+	
+	# BSS declarations emit accessors.
+	.comm	aes_128_ctr_generic_storage,64,32
+	.lcomm	aes_128_ctr_generic_storage2,64,32
+	
+	# BSS symbols may also be emitted in .bss sections.
+	.section .bss,"awT",@nobits
+	.align 4
+	.globl x
+	.type   x, @object
+	.size   x, 4
+x:
+.Lx_local_target:
+
+	.zero 4
+.Llocal:
+	.quad 0
+	.size .Llocal, 4
+
+	# .bss handling is terminated by a .text directive.
+	.text
+	.section .bss,"awT",@nobits
+y:
+.Ly_local_target:
+
+	.quad 0
+
+	# Or a .section directive.
+# WAS .section .rodata
+.text
+	.quad 0
+
+	# Or the end of the file.
+	.section .bss,"awT",@nobits
+z:
+.Lz_local_target:
+
+	.quad 0
+.text
+BORINGSSL_bcm_text_end:
+.type aes_128_ctr_generic_storage_bss_get, @function
+aes_128_ctr_generic_storage_bss_get:
+	leaq	aes_128_ctr_generic_storage(%rip), %rax
+	ret
+.type aes_128_ctr_generic_storage2_bss_get, @function
+aes_128_ctr_generic_storage2_bss_get:
+	leaq	aes_128_ctr_generic_storage2(%rip), %rax
+	ret
+.type x_bss_get, @function
+x_bss_get:
+	leaq	.Lx_local_target(%rip), %rax
+	ret
+.type y_bss_get, @function
+y_bss_get:
+	leaq	.Ly_local_target(%rip), %rax
+	ret
+.type z_bss_get, @function
+z_bss_get:
+	leaq	.Lz_local_target(%rip), %rax
+	ret
+.type OPENSSL_ia32cap_get, @function
+OPENSSL_ia32cap_get:
+	leaq OPENSSL_ia32cap_P(%rip), %rax
+	ret
+.extern OPENSSL_ia32cap_P
+.type OPENSSL_ia32cap_addr_delta, @object
+.size OPENSSL_ia32cap_addr_delta, 8
+OPENSSL_ia32cap_addr_delta:
+.quad OPENSSL_ia32cap_P-OPENSSL_ia32cap_addr_delta
+.type BORINGSSL_bcm_text_hash, @object
+.size BORINGSSL_bcm_text_hash, 64
+BORINGSSL_bcm_text_hash:
+.byte 0xae
+.byte 0x2c
+.byte 0xea
+.byte 0x2a
+.byte 0xbd
+.byte 0xa6
+.byte 0xf3
+.byte 0xec
+.byte 0x97
+.byte 0x7f
+.byte 0x9b
+.byte 0xf6
+.byte 0x94
+.byte 0x9a
+.byte 0xfc
+.byte 0x83
+.byte 0x68
+.byte 0x27
+.byte 0xcb
+.byte 0xa0
+.byte 0xa0
+.byte 0x9f
+.byte 0x6b
+.byte 0x6f
+.byte 0xde
+.byte 0x52
+.byte 0xcd
+.byte 0xe2
+.byte 0xcd
+.byte 0xff
+.byte 0x31
+.byte 0x80
+.byte 0xa2
+.byte 0xd4
+.byte 0xc3
+.byte 0x66
+.byte 0xf
+.byte 0xc2
+.byte 0x6a
+.byte 0x7b
+.byte 0xf4
+.byte 0xbe
+.byte 0x39
+.byte 0xa2
+.byte 0xd7
+.byte 0x25
+.byte 0xdb
+.byte 0x21
+.byte 0x98
+.byte 0xe9
+.byte 0xd5
+.byte 0x53
+.byte 0xbf
+.byte 0x5c
+.byte 0x32
+.byte 0x6
+.byte 0x83
+.byte 0x34
+.byte 0xc
+.byte 0x65
+.byte 0x89
+.byte 0x52
+.byte 0xbd
+.byte 0x1f
diff --git a/util/fipstools/testdata/x86_64-Basic/in.s b/util/fipstools/testdata/x86_64-Basic/in.s
new file mode 100644
index 0000000..e31c370
--- /dev/null
+++ b/util/fipstools/testdata/x86_64-Basic/in.s
@@ -0,0 +1,43 @@
+	# Most instructions and lines should pass unaltered. This is made up of
+	# copy-and-pasted bits of compiler output and likely does not actually
+	# run.
+	.file "bcm.c"
+	.text
+
+	.type foo, @function
+	.globl foo
+foo:
+	.file 1 "../foo/bar.c"
+	.loc 1 2 3
+	.cfi_startproc
+	pushq %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq %rsp, %rbp
+	movq %rdi, -24(%rbp)
+	movq -24(%rbp), %rax
+	.loc 1 168 0 is_stmt 0 discriminator 1
+	cmpq	-8(%rbp), %rax
+	jmpq *%rax
+        movdqa  %xmm3,%xmm10
+	psrlq   $1,%xmm3
+	pxor    %xmm6,%xmm5
+	pxor    %xmm4,%xmm3
+	pand    %xmm7,%xmm5
+	pand    %xmm7,%xmm3
+        pxor    %xmm5,%xmm6
+	paddd   112(%r11),%xmm15
+	vmovdqa %xmm0,%xmm5
+	vpunpckhqdq     %xmm0,%xmm0,%xmm3
+	vpxor   %xmm0,%xmm3,%xmm3
+	vpclmulqdq      $0x11,%xmm2,%xmm0,%xmm1
+	vpclmulqdq      $0x00,%xmm2,%xmm0,%xmm0
+	vpclmulqdq      $0x00,%xmm6,%xmm3,%xmm3
+	vpxor   %xmm0,%xmm1,%xmm4
+	vpxor   %xmm4,%xmm3,%xmm3
+	.byte   0xf3,0xc3
+	movq %rax, %rbx # Comments can be on the same line as an instruction.
+.L3: # Or on the same line as a label.
+.L4: .L5:	movq %rbx, %rax # This is also legal.
+.size	foo, .-foo
+.type	foo, @function
diff --git a/util/fipstools/testdata/x86_64-Basic/out.s b/util/fipstools/testdata/x86_64-Basic/out.s
new file mode 100644
index 0000000..040d8af
--- /dev/null
+++ b/util/fipstools/testdata/x86_64-Basic/out.s
@@ -0,0 +1,127 @@
+.text
+BORINGSSL_bcm_text_start:
+	# Most instructions and lines should pass unaltered. This is made up of
+	# copy-and-pasted bits of compiler output and likely does not actually
+	# run.
+	.file "bcm.c"
+	.text
+
+	.type foo, @function
+	.globl foo
+.Lfoo_local_target:
+foo:
+	.file 1 "../foo/bar.c"
+	.loc 1 2 3
+	.cfi_startproc
+	pushq %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq %rsp, %rbp
+	movq %rdi, -24(%rbp)
+	movq -24(%rbp), %rax
+	.loc 1 168 0 is_stmt 0 discriminator 1
+	cmpq	-8(%rbp), %rax
+	jmpq *%rax
+        movdqa  %xmm3,%xmm10
+	psrlq   $1,%xmm3
+	pxor    %xmm6,%xmm5
+	pxor    %xmm4,%xmm3
+	pand    %xmm7,%xmm5
+	pand    %xmm7,%xmm3
+        pxor    %xmm5,%xmm6
+	paddd   112(%r11),%xmm15
+	vmovdqa %xmm0,%xmm5
+	vpunpckhqdq     %xmm0,%xmm0,%xmm3
+	vpxor   %xmm0,%xmm3,%xmm3
+	vpclmulqdq      $0x11,%xmm2,%xmm0,%xmm1
+	vpclmulqdq      $0x00,%xmm2,%xmm0,%xmm0
+	vpclmulqdq      $0x00,%xmm6,%xmm3,%xmm3
+	vpxor   %xmm0,%xmm1,%xmm4
+	vpxor   %xmm4,%xmm3,%xmm3
+	.byte   0xf3,0xc3
+	movq %rax, %rbx # Comments can be on the same line as an instruction.
+.L3:
+ # Or on the same line as a label.
+.L4:
+.L5:
+	movq %rbx, %rax # This is also legal.
+.size	foo, .-foo
+.type	foo, @function
+.text
+BORINGSSL_bcm_text_end:
+.type OPENSSL_ia32cap_get, @function
+OPENSSL_ia32cap_get:
+	leaq OPENSSL_ia32cap_P(%rip), %rax
+	ret
+.extern OPENSSL_ia32cap_P
+.type OPENSSL_ia32cap_addr_delta, @object
+.size OPENSSL_ia32cap_addr_delta, 8
+OPENSSL_ia32cap_addr_delta:
+.quad OPENSSL_ia32cap_P-OPENSSL_ia32cap_addr_delta
+.type BORINGSSL_bcm_text_hash, @object
+.size BORINGSSL_bcm_text_hash, 64
+BORINGSSL_bcm_text_hash:
+.byte 0xae
+.byte 0x2c
+.byte 0xea
+.byte 0x2a
+.byte 0xbd
+.byte 0xa6
+.byte 0xf3
+.byte 0xec
+.byte 0x97
+.byte 0x7f
+.byte 0x9b
+.byte 0xf6
+.byte 0x94
+.byte 0x9a
+.byte 0xfc
+.byte 0x83
+.byte 0x68
+.byte 0x27
+.byte 0xcb
+.byte 0xa0
+.byte 0xa0
+.byte 0x9f
+.byte 0x6b
+.byte 0x6f
+.byte 0xde
+.byte 0x52
+.byte 0xcd
+.byte 0xe2
+.byte 0xcd
+.byte 0xff
+.byte 0x31
+.byte 0x80
+.byte 0xa2
+.byte 0xd4
+.byte 0xc3
+.byte 0x66
+.byte 0xf
+.byte 0xc2
+.byte 0x6a
+.byte 0x7b
+.byte 0xf4
+.byte 0xbe
+.byte 0x39
+.byte 0xa2
+.byte 0xd7
+.byte 0x25
+.byte 0xdb
+.byte 0x21
+.byte 0x98
+.byte 0xe9
+.byte 0xd5
+.byte 0x53
+.byte 0xbf
+.byte 0x5c
+.byte 0x32
+.byte 0x6
+.byte 0x83
+.byte 0x34
+.byte 0xc
+.byte 0x65
+.byte 0x89
+.byte 0x52
+.byte 0xbd
+.byte 0x1f
diff --git a/util/fipstools/testdata/x86_64-GOTRewrite/in.s b/util/fipstools/testdata/x86_64-GOTRewrite/in.s
new file mode 100644
index 0000000..60ef13a
--- /dev/null
+++ b/util/fipstools/testdata/x86_64-GOTRewrite/in.s
@@ -0,0 +1,31 @@
+	.text
+foo:
+	# leaq of OPENSSL_ia32cap_P is supported.
+	leaq OPENSSL_ia32cap_P(%rip), %r11
+
+	# As is the equivalent GOTPCREL movq.
+	movq OPENSSL_ia32cap_P@GOTPCREL(%rip), %r12
+
+	# Test that GOTPCREL accesses get translated. They are handled
+	# differently for local and external symbols.
+
+	pushq stderr@GOTPCREL(%rip)
+	pushq foo@GOTPCREL(%rip)
+
+	movq stderr@GOTPCREL(%rip), %r11
+	movq foo@GOTPCREL(%rip), %r11
+
+	vmovq stderr@GOTPCREL(%rip), %xmm0
+	vmovq foo@GOTPCREL(%rip), %xmm0
+
+	cmoveq stderr@GOTPCREL(%rip), %r11
+	cmoveq foo@GOTPCREL(%rip), %r11
+	cmovneq stderr@GOTPCREL(%rip), %r11
+	cmovneq foo@GOTPCREL(%rip), %r11
+
+	# Synthesized symbols do not use the GOT.
+	movq BORINGSSL_bcm_text_start@GOTPCREL(%rip), %r11
+	movq foobar_bss_get@GOTPCREL(%rip), %r11
+	movq OPENSSL_ia32cap_get@GOTPCREL(%rip), %r11
+
+.comm foobar,64,32
diff --git a/util/fipstools/testdata/x86_64-GOTRewrite/out.s b/util/fipstools/testdata/x86_64-GOTRewrite/out.s
new file mode 100644
index 0000000..35fe6c5
--- /dev/null
+++ b/util/fipstools/testdata/x86_64-GOTRewrite/out.s
@@ -0,0 +1,196 @@
+.text
+BORINGSSL_bcm_text_start:
+	.text
+.Lfoo_local_target:
+foo:
+	# leaq of OPENSSL_ia32cap_P is supported.
+# WAS leaq OPENSSL_ia32cap_P(%rip), %r11
+	leaq	-128(%rsp), %rsp
+	pushfq
+	leaq	OPENSSL_ia32cap_addr_delta(%rip), %r11
+	addq	(%r11), %r11
+	popfq
+	leaq	128(%rsp), %rsp
+
+	# As is the equivalent GOTPCREL movq.
+# WAS movq OPENSSL_ia32cap_P@GOTPCREL(%rip), %r12
+	leaq	-128(%rsp), %rsp
+	pushfq
+	leaq	OPENSSL_ia32cap_addr_delta(%rip), %r12
+	addq	(%r12), %r12
+	popfq
+	leaq	128(%rsp), %rsp
+
+	# Test that GOTPCREL accesses get translated. They are handled
+	# differently for local and external symbols.
+
+# WAS pushq stderr@GOTPCREL(%rip)
+	pushq %rax
+	leaq -128(%rsp), %rsp
+	pushf
+	leaq stderr_GOTPCREL_external(%rip), %rax
+	addq (%rax), %rax
+	movq (%rax), %rax
+	popf
+	leaq	128(%rsp), %rsp
+	xchg %rax, (%rsp)
+# WAS pushq foo@GOTPCREL(%rip)
+	pushq %rax
+	leaq	.Lfoo_local_target(%rip), %rax
+	xchg %rax, (%rsp)
+
+# WAS movq stderr@GOTPCREL(%rip), %r11
+	leaq -128(%rsp), %rsp
+	pushf
+	leaq stderr_GOTPCREL_external(%rip), %r11
+	addq (%r11), %r11
+	movq (%r11), %r11
+	popf
+	leaq	128(%rsp), %rsp
+# WAS movq foo@GOTPCREL(%rip), %r11
+	leaq	.Lfoo_local_target(%rip), %r11
+
+# WAS vmovq stderr@GOTPCREL(%rip), %xmm0
+	leaq -128(%rsp), %rsp
+	pushq %rax
+	pushf
+	leaq stderr_GOTPCREL_external(%rip), %rax
+	addq (%rax), %rax
+	movq (%rax), %rax
+	popf
+	movq %rax, %xmm0
+	popq %rax
+	leaq 128(%rsp), %rsp
+# WAS vmovq foo@GOTPCREL(%rip), %xmm0
+	leaq -128(%rsp), %rsp
+	pushq %rax
+	leaq	.Lfoo_local_target(%rip), %rax
+	movq %rax, %xmm0
+	popq %rax
+	leaq 128(%rsp), %rsp
+
+# WAS cmoveq stderr@GOTPCREL(%rip), %r11
+	jne 999f
+	leaq -128(%rsp), %rsp
+	pushf
+	leaq stderr_GOTPCREL_external(%rip), %r11
+	addq (%r11), %r11
+	movq (%r11), %r11
+	popf
+	leaq	128(%rsp), %rsp
+999:
+# WAS cmoveq foo@GOTPCREL(%rip), %r11
+	jne 999f
+	leaq	.Lfoo_local_target(%rip), %r11
+999:
+# WAS cmovneq stderr@GOTPCREL(%rip), %r11
+	je 999f
+	leaq -128(%rsp), %rsp
+	pushf
+	leaq stderr_GOTPCREL_external(%rip), %r11
+	addq (%r11), %r11
+	movq (%r11), %r11
+	popf
+	leaq	128(%rsp), %rsp
+999:
+# WAS cmovneq foo@GOTPCREL(%rip), %r11
+	je 999f
+	leaq	.Lfoo_local_target(%rip), %r11
+999:
+
+	# Synthesized symbols do not use the GOT.
+# WAS movq BORINGSSL_bcm_text_start@GOTPCREL(%rip), %r11
+	leaq	BORINGSSL_bcm_text_start(%rip), %r11
+# WAS movq foobar_bss_get@GOTPCREL(%rip), %r11
+	leaq	foobar_bss_get(%rip), %r11
+# WAS movq OPENSSL_ia32cap_get@GOTPCREL(%rip), %r11
+	leaq	OPENSSL_ia32cap_get(%rip), %r11
+
+.comm foobar,64,32
+.text
+BORINGSSL_bcm_text_end:
+.type foobar_bss_get, @function
+foobar_bss_get:
+	leaq	foobar(%rip), %rax
+	ret
+.type stderr_GOTPCREL_external, @object
+.size stderr_GOTPCREL_external, 8
+stderr_GOTPCREL_external:
+	.long stderr@GOTPCREL
+	.long 0
+.type OPENSSL_ia32cap_get, @function
+OPENSSL_ia32cap_get:
+	leaq OPENSSL_ia32cap_P(%rip), %rax
+	ret
+.extern OPENSSL_ia32cap_P
+.type OPENSSL_ia32cap_addr_delta, @object
+.size OPENSSL_ia32cap_addr_delta, 8
+OPENSSL_ia32cap_addr_delta:
+.quad OPENSSL_ia32cap_P-OPENSSL_ia32cap_addr_delta
+.type BORINGSSL_bcm_text_hash, @object
+.size BORINGSSL_bcm_text_hash, 64
+BORINGSSL_bcm_text_hash:
+.byte 0xae
+.byte 0x2c
+.byte 0xea
+.byte 0x2a
+.byte 0xbd
+.byte 0xa6
+.byte 0xf3
+.byte 0xec
+.byte 0x97
+.byte 0x7f
+.byte 0x9b
+.byte 0xf6
+.byte 0x94
+.byte 0x9a
+.byte 0xfc
+.byte 0x83
+.byte 0x68
+.byte 0x27
+.byte 0xcb
+.byte 0xa0
+.byte 0xa0
+.byte 0x9f
+.byte 0x6b
+.byte 0x6f
+.byte 0xde
+.byte 0x52
+.byte 0xcd
+.byte 0xe2
+.byte 0xcd
+.byte 0xff
+.byte 0x31
+.byte 0x80
+.byte 0xa2
+.byte 0xd4
+.byte 0xc3
+.byte 0x66
+.byte 0xf
+.byte 0xc2
+.byte 0x6a
+.byte 0x7b
+.byte 0xf4
+.byte 0xbe
+.byte 0x39
+.byte 0xa2
+.byte 0xd7
+.byte 0x25
+.byte 0xdb
+.byte 0x21
+.byte 0x98
+.byte 0xe9
+.byte 0xd5
+.byte 0x53
+.byte 0xbf
+.byte 0x5c
+.byte 0x32
+.byte 0x6
+.byte 0x83
+.byte 0x34
+.byte 0xc
+.byte 0x65
+.byte 0x89
+.byte 0x52
+.byte 0xbd
+.byte 0x1f
diff --git a/util/fipstools/testdata/x86_64-LabelRewrite/in1.s b/util/fipstools/testdata/x86_64-LabelRewrite/in1.s
new file mode 100644
index 0000000..e97b280
--- /dev/null
+++ b/util/fipstools/testdata/x86_64-LabelRewrite/in1.s
@@ -0,0 +1,43 @@
+	.type foo, @function
+	.globl foo
+foo:
+	movq $0, %rax
+	ret
+
+bar:
+	# References to globals must be rewritten to their local targets.
+	call foo
+	jmp foo
+	jbe foo
+	jne foo
+
+	# Jumps to PLT symbols are rewritten through redirectors.
+	call memcpy@PLT
+	jmp memcpy@PLT
+	jbe memcpy@PLT
+
+	# Jumps to local PLT symbols use their local targets.
+	call foo@PLT
+	jmp foo@PLT
+	jbe foo@PLT
+
+	# Synthesized symbols are treated as local ones.
+	call OPENSSL_ia32cap_get@PLT
+
+	# References to local labels are left as-is in the first file.
+.Llocal_label:
+	jbe .Llocal_label
+	leaq .Llocal_label+2048(%rip), %r14
+	leaq .Llocal_label+2048+1024(%rip), %r14
+
+	.section .rodata
+.L1:
+	.quad 42
+.L2:
+	.quad .L2-.L1
+
+	# Local labels and their jumps are left alone.
+	.text
+	jmp 1f
+1:
+	jmp 1b
diff --git a/util/fipstools/testdata/x86_64-LabelRewrite/in2.s b/util/fipstools/testdata/x86_64-LabelRewrite/in2.s
new file mode 100644
index 0000000..7a5a551
--- /dev/null
+++ b/util/fipstools/testdata/x86_64-LabelRewrite/in2.s
@@ -0,0 +1,12 @@
+	# References to local labels are rewrittenn in subsequent files.
+.Llocal_label:
+	jbe .Llocal_label
+	leaq .Llocal_label+2048(%rip), %r14
+	leaq .Llocal_label+2048+1024(%rip), %r14
+
+	.section .rodata
+.L1:
+	.quad 42
+.L2:
+	.quad .L2-.L1
+
diff --git a/util/fipstools/testdata/x86_64-LabelRewrite/out.s b/util/fipstools/testdata/x86_64-LabelRewrite/out.s
new file mode 100644
index 0000000..5dd8feb
--- /dev/null
+++ b/util/fipstools/testdata/x86_64-LabelRewrite/out.s
@@ -0,0 +1,164 @@
+.text
+BORINGSSL_bcm_text_start:
+	.type foo, @function
+	.globl foo
+.Lfoo_local_target:
+foo:
+	movq $0, %rax
+	ret
+
+.Lbar_local_target:
+bar:
+	# References to globals must be rewritten to their local targets.
+# WAS call foo
+	call	.Lfoo_local_target
+# WAS jmp foo
+	jmp	.Lfoo_local_target
+# WAS jbe foo
+	jbe	.Lfoo_local_target
+# WAS jne foo
+	jne	.Lfoo_local_target
+
+	# Jumps to PLT symbols are rewritten through redirectors.
+# WAS call memcpy@PLT
+	call	bcm_redirector_memcpy
+# WAS jmp memcpy@PLT
+	jmp	bcm_redirector_memcpy
+# WAS jbe memcpy@PLT
+	jbe	bcm_redirector_memcpy
+
+	# Jumps to local PLT symbols use their local targets.
+# WAS call foo@PLT
+	call	.Lfoo_local_target
+# WAS jmp foo@PLT
+	jmp	.Lfoo_local_target
+# WAS jbe foo@PLT
+	jbe	.Lfoo_local_target
+
+	# Synthesized symbols are treated as local ones.
+# WAS call OPENSSL_ia32cap_get@PLT
+	call	OPENSSL_ia32cap_get
+
+	# References to local labels are left as-is in the first file.
+.Llocal_label:
+
+	jbe .Llocal_label
+	leaq .Llocal_label+2048(%rip), %r14
+	leaq .Llocal_label+2048+1024(%rip), %r14
+
+# WAS .section .rodata
+.text
+.L1:
+
+	.quad 42
+.L2:
+
+	.quad .L2-.L1
+
+	# Local labels and their jumps are left alone.
+	.text
+	jmp 1f
+1:
+
+	jmp 1b
+	# References to local labels are rewrittenn in subsequent files.
+.Llocal_label_BCM_1:
+
+# WAS jbe .Llocal_label
+	jbe	.Llocal_label_BCM_1
+# WAS leaq .Llocal_label+2048(%rip), %r14
+	leaq	.Llocal_label_BCM_1+2048(%rip), %r14
+# WAS leaq .Llocal_label+2048+1024(%rip), %r14
+	leaq	.Llocal_label_BCM_1+2048+1024(%rip), %r14
+
+# WAS .section .rodata
+.text
+.L1_BCM_1:
+
+	.quad 42
+.L2_BCM_1:
+
+# WAS .quad .L2-.L1
+	.quad	.L2_BCM_1-.L1_BCM_1
+
+.text
+BORINGSSL_bcm_text_end:
+.type bcm_redirector_memcpy, @function
+bcm_redirector_memcpy:
+	jmp	memcpy@PLT
+.type OPENSSL_ia32cap_get, @function
+OPENSSL_ia32cap_get:
+	leaq OPENSSL_ia32cap_P(%rip), %rax
+	ret
+.extern OPENSSL_ia32cap_P
+.type OPENSSL_ia32cap_addr_delta, @object
+.size OPENSSL_ia32cap_addr_delta, 8
+OPENSSL_ia32cap_addr_delta:
+.quad OPENSSL_ia32cap_P-OPENSSL_ia32cap_addr_delta
+.type BORINGSSL_bcm_text_hash, @object
+.size BORINGSSL_bcm_text_hash, 64
+BORINGSSL_bcm_text_hash:
+.byte 0xae
+.byte 0x2c
+.byte 0xea
+.byte 0x2a
+.byte 0xbd
+.byte 0xa6
+.byte 0xf3
+.byte 0xec
+.byte 0x97
+.byte 0x7f
+.byte 0x9b
+.byte 0xf6
+.byte 0x94
+.byte 0x9a
+.byte 0xfc
+.byte 0x83
+.byte 0x68
+.byte 0x27
+.byte 0xcb
+.byte 0xa0
+.byte 0xa0
+.byte 0x9f
+.byte 0x6b
+.byte 0x6f
+.byte 0xde
+.byte 0x52
+.byte 0xcd
+.byte 0xe2
+.byte 0xcd
+.byte 0xff
+.byte 0x31
+.byte 0x80
+.byte 0xa2
+.byte 0xd4
+.byte 0xc3
+.byte 0x66
+.byte 0xf
+.byte 0xc2
+.byte 0x6a
+.byte 0x7b
+.byte 0xf4
+.byte 0xbe
+.byte 0x39
+.byte 0xa2
+.byte 0xd7
+.byte 0x25
+.byte 0xdb
+.byte 0x21
+.byte 0x98
+.byte 0xe9
+.byte 0xd5
+.byte 0x53
+.byte 0xbf
+.byte 0x5c
+.byte 0x32
+.byte 0x6
+.byte 0x83
+.byte 0x34
+.byte 0xc
+.byte 0x65
+.byte 0x89
+.byte 0x52
+.byte 0xbd
+.byte 0x1f
diff --git a/util/fipstools/testdata/x86_64-Sections/in.s b/util/fipstools/testdata/x86_64-Sections/in.s
new file mode 100644
index 0000000..8358a4c
--- /dev/null
+++ b/util/fipstools/testdata/x86_64-Sections/in.s
@@ -0,0 +1,36 @@
+	# .text stays in .text
+	.text
+	movq %rax, %rax
+
+	# -ffunction-sections is undone.
+	.section .text.foo,"ax",@progbits
+	.globl foo
+foo:
+	ret
+
+	# .rodata is moved to .text.
+	.section .rodata
+	.long 42
+	.string "Hello world, esc\ape characters are \"fun\"\\"
+
+	# Compilers sometimes emit extra rodata sections.
+	.section .rodata.str1.1,"aMS",@progbits,1
+	.string "NIST P-256"
+	.text
+
+	# A number of sections are left alone.
+	.section	.init_array,"aw"
+	.align 8
+	.quad	foo
+	.section	.rodata
+	.align 16
+	.section	.debug_info,"",@progbits
+.Ldebug_info0:
+	.long	0x1b35e
+	.value	0x4
+	.long	.L1
+	.byte	0x8
+	.uleb128 0x1
+	.long	.L2
+	.byte	0x1
+	.long	.L3
diff --git a/util/fipstools/testdata/x86_64-Sections/out.s b/util/fipstools/testdata/x86_64-Sections/out.s
new file mode 100644
index 0000000..768fef3
--- /dev/null
+++ b/util/fipstools/testdata/x86_64-Sections/out.s
@@ -0,0 +1,122 @@
+.text
+BORINGSSL_bcm_text_start:
+	# .text stays in .text
+	.text
+	movq %rax, %rax
+
+	# -ffunction-sections is undone.
+# WAS .section .text.foo,"ax",@progbits
+.text
+	.globl foo
+.Lfoo_local_target:
+foo:
+	ret
+
+	# .rodata is moved to .text.
+# WAS .section .rodata
+.text
+	.long 42
+	.string "Hello world, esc\ape characters are \"fun\"\\"
+
+	# Compilers sometimes emit extra rodata sections.
+# WAS .section .rodata.str1.1,"aMS",@progbits,1
+.text
+	.string "NIST P-256"
+	.text
+
+	# A number of sections are left alone.
+	.section	.init_array,"aw"
+	.align 8
+	.quad	foo
+# WAS .section	.rodata
+.text
+	.align 16
+	.section	.debug_info,"",@progbits
+.Ldebug_info0:
+
+	.long	0x1b35e
+	.value	0x4
+	.long	.L1
+	.byte	0x8
+	.uleb128 0x1
+	.long	.L2
+	.byte	0x1
+	.long	.L3
+.text
+BORINGSSL_bcm_text_end:
+.type OPENSSL_ia32cap_get, @function
+OPENSSL_ia32cap_get:
+	leaq OPENSSL_ia32cap_P(%rip), %rax
+	ret
+.extern OPENSSL_ia32cap_P
+.type OPENSSL_ia32cap_addr_delta, @object
+.size OPENSSL_ia32cap_addr_delta, 8
+OPENSSL_ia32cap_addr_delta:
+.quad OPENSSL_ia32cap_P-OPENSSL_ia32cap_addr_delta
+.type BORINGSSL_bcm_text_hash, @object
+.size BORINGSSL_bcm_text_hash, 64
+BORINGSSL_bcm_text_hash:
+.byte 0xae
+.byte 0x2c
+.byte 0xea
+.byte 0x2a
+.byte 0xbd
+.byte 0xa6
+.byte 0xf3
+.byte 0xec
+.byte 0x97
+.byte 0x7f
+.byte 0x9b
+.byte 0xf6
+.byte 0x94
+.byte 0x9a
+.byte 0xfc
+.byte 0x83
+.byte 0x68
+.byte 0x27
+.byte 0xcb
+.byte 0xa0
+.byte 0xa0
+.byte 0x9f
+.byte 0x6b
+.byte 0x6f
+.byte 0xde
+.byte 0x52
+.byte 0xcd
+.byte 0xe2
+.byte 0xcd
+.byte 0xff
+.byte 0x31
+.byte 0x80
+.byte 0xa2
+.byte 0xd4
+.byte 0xc3
+.byte 0x66
+.byte 0xf
+.byte 0xc2
+.byte 0x6a
+.byte 0x7b
+.byte 0xf4
+.byte 0xbe
+.byte 0x39
+.byte 0xa2
+.byte 0xd7
+.byte 0x25
+.byte 0xdb
+.byte 0x21
+.byte 0x98
+.byte 0xe9
+.byte 0xd5
+.byte 0x53
+.byte 0xbf
+.byte 0x5c
+.byte 0x32
+.byte 0x6
+.byte 0x83
+.byte 0x34
+.byte 0xc
+.byte 0x65
+.byte 0x89
+.byte 0x52
+.byte 0xbd
+.byte 0x1f