1//
2// OCCAM
3//
4// Copyright (c) 2017, SRI International
5//
6//  All rights reserved.
7//
8// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are met:
10//
11// * Redistributions of source code must retain the above copyright notice, this
12//   list of conditions and the following disclaimer.
13//
14// * Redistributions in binary form must reproduce the above copyright notice,
15//   this list of conditions and the following disclaimer in the documentation
16//   and/or other materials provided with the distribution.
17//
18// * Neither the name of SRI International nor the names of its contributors may
19//   be used to endorse or promote products derived from this software without
20//   specific prior written permission.
21//
22// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32//
33
34package shared
35
36import (
37	"bytes"
38	"debug/elf"
39	"debug/macho"
40	"flag"
41	"fmt"
42	"io/ioutil"
43	"os"
44	"os/exec"
45	"path"
46	"path/filepath"
47	"runtime"
48	"sort"
49	"strconv"
50	"strings"
51)
52
53//ExtractionArgs encapsulate the results of parsing the commandline options
54type ExtractionArgs struct {
55	Failure             bool // indicates failure in parsing the cmd line args
56	Verbose             bool // inform the user of what is going on
57	WriteManifest       bool // write a manifest of bitcode files used
58	SortBitcodeFiles    bool // sort the arguments to linking and archiving (debugging too)
59	BuildBitcodeModule  bool // buld an archive rather than a module
60	KeepTemp            bool // keep temporary linking folder
61	StrictExtract       bool // turn extraction failures into errors
62	LinkArgSize         int  // maximum size of a llvm-link command line
63	InputType           int
64	ObjectTypeInArchive int // Type of file that can be put into an archive
65	InputFile           string
66	OutputFile          string
67	LlvmLinkerName      string
68	LlvmArchiverName    string
69	ArchiverName        string
70	ArArgs              []string
71	Extractor           func(string) ([]string, bool)
72}
73
74//for printing out the parsed arguments, some have been skipped.
75func (ea ExtractionArgs) String() string {
76	format :=
77		`
78ea.Verbose:            %v
79ea.WriteManifest:      %v
80ea.SortBitcodeFiles:   %v
81ea.BuildBitcodeModule: %v
82ea.KeepTemp:           %v
83ea.LinkArgSize:        %v
84ea.InputFile:          %v
85ea.OutputFile:         %v
86ea.LlvmArchiverName:   %v
87ea.LlvmLinkerName:     %v
88ea.ArchiverName:       %v
89ea.StrictExtract:      %v
90`
91	return fmt.Sprintf(format, ea.Verbose, ea.WriteManifest, ea.SortBitcodeFiles, ea.BuildBitcodeModule,
92		ea.KeepTemp, ea.LinkArgSize, ea.InputFile, ea.OutputFile, ea.LlvmArchiverName,
93		ea.LlvmLinkerName, ea.ArchiverName, ea.StrictExtract)
94}
95
96//ParseSwitches parses the command line into an ExtractionArgs object.
97func ParseSwitches(args []string) (ea ExtractionArgs) {
98
99	var flagSet *flag.FlagSet = flag.NewFlagSet(args[0], flag.ContinueOnError)
100
101	flagSet.BoolVar(&ea.Verbose, "v", false, "verbose mode")
102	flagSet.BoolVar(&ea.WriteManifest, "m", false, "write the manifest")
103	flagSet.BoolVar(&ea.SortBitcodeFiles, "s", false, "sort the bitcode files")
104	flagSet.BoolVar(&ea.BuildBitcodeModule, "b", false, "build a bitcode module")
105	flagSet.StringVar(&ea.OutputFile, "o", "", "the output file")
106	flagSet.StringVar(&ea.LlvmArchiverName, "a", "llvm-ar", "the llvm archiver (i.e. llvm-ar)")
107	flagSet.StringVar(&ea.ArchiverName, "r", "ar", "the system archiver (i.e. ar)")
108	flagSet.StringVar(&ea.LlvmLinkerName, "l", "llvm-link", "the llvm linker (i.e. llvm-link)")
109	flagSet.IntVar(&ea.LinkArgSize, "n", 0, "maximum llvm-link command line size (in bytes)")
110	flagSet.BoolVar(&ea.KeepTemp, "t", false, "keep temporary linking folder")
111	flagSet.BoolVar(&ea.StrictExtract, "S", false, "exit with an error if extraction fails")
112
113	err := flagSet.Parse(args[1:])
114
115	if err != nil {
116		ea.Failure = true
117		return
118	}
119
120	ea.LlvmArchiverName = resolveTool("llvm-ar", LLVMARName, ea.LlvmArchiverName)
121	ea.LlvmLinkerName = resolveTool("llvm-link", LLVMLINKName, ea.LlvmLinkerName)
122	inputFiles := flagSet.Args()
123	if len(inputFiles) != 1 {
124		LogError("Can currently only deal with exactly one input file, sorry. You gave me %v input files.\n", len(inputFiles))
125		ea.Failure = true
126		return
127	}
128	ea.InputFile = inputFiles[0]
129	if _, err := os.Stat(ea.InputFile); os.IsNotExist(err) {
130		LogError("The input file %s  does not exist.", ea.InputFile)
131		ea.Failure = true
132		return
133	}
134	realPath, err := filepath.EvalSymlinks(ea.InputFile)
135	if err != nil {
136		LogError("There was an error getting the real path of %s.", ea.InputFile)
137		ea.Failure = true
138		return
139	}
140	ea.InputFile = realPath
141	ea.InputType, _ = getFileType(realPath)
142
143	LogInfo("%v", ea)
144
145	return
146}
147
148//Extract extracts the LLVM bitcode according to the arguments it is passed.
149func Extract(args []string) (exitCode int) {
150
151	exitCode = 1
152
153	ea := ParseSwitches(args)
154
155	if ea.Failure {
156		return
157	}
158
159	// Set arguments according to runtime OS
160	success := setPlatform(&ea)
161	if !success {
162		return
163	}
164
165	// Create output filename if not given
166	setOutputFile(&ea)
167
168	switch ea.InputType {
169	case fileTypeELFEXECUTABLE,
170		fileTypeELFSHARED,
171		fileTypeELFOBJECT,
172		fileTypeMACHEXECUTABLE,
173		fileTypeMACHSHARED,
174		fileTypeMACHOBJECT:
175		success = handleExecutable(ea)
176	case fileTypeARCHIVE:
177		success = handleArchive(ea)
178	case fileTypeTHINARCHIVE:
179		success = handleThinArchive(ea)
180	case fileTypeERROR:
181	default:
182		LogError("Incorrect input file type %v.", ea.InputType)
183		return
184	}
185
186	if success {
187		exitCode = 0
188	}
189
190	return
191}
192
193// Set arguments according to runtime OS
194func setPlatform(ea *ExtractionArgs) (success bool) {
195	switch platform := runtime.GOOS; platform {
196	case osFREEBSD, osLINUX:
197		ea.Extractor = extractSectionUnix
198		if ea.Verbose {
199			ea.ArArgs = append(ea.ArArgs, "xv")
200		} else {
201			ea.ArArgs = append(ea.ArArgs, "x")
202		}
203		ea.ObjectTypeInArchive = fileTypeELFOBJECT
204		success = true
205	case osDARWIN:
206		ea.Extractor = extractSectionDarwin
207		ea.ArArgs = append(ea.ArArgs, "-x")
208		if ea.Verbose {
209			ea.ArArgs = append(ea.ArArgs, "-v")
210		}
211		ea.ObjectTypeInArchive = fileTypeMACHOBJECT
212		success = true
213	default:
214		LogError("Unsupported platform: %s.", platform)
215	}
216	return
217}
218
219// Create output filename if not given
220func setOutputFile(ea *ExtractionArgs) {
221	if ea.OutputFile == "" {
222		if ea.InputType == fileTypeARCHIVE || ea.InputType == fileTypeTHINARCHIVE {
223			var ext string
224			if ea.BuildBitcodeModule {
225				ext = ".a.bc"
226			} else {
227				ext = ".bca"
228			}
229			ea.OutputFile = strings.TrimSuffix(ea.InputFile, ".a") + ext
230		} else {
231			ea.OutputFile = ea.InputFile + ".bc"
232		}
233	}
234}
235
236func resolveTool(defaultPath string, envPath string, usrPath string) (path string) {
237	if usrPath != defaultPath {
238		path = usrPath
239	} else {
240		if LLVMToolChainBinDir != "" {
241			if envPath != "" {
242				path = filepath.Join(LLVMToolChainBinDir, envPath)
243			} else {
244				path = filepath.Join(LLVMToolChainBinDir, defaultPath)
245			}
246		} else {
247			if envPath != "" {
248				path = envPath
249			} else {
250				path = defaultPath
251			}
252		}
253	}
254	LogDebug("defaultPath = %s", defaultPath)
255	LogDebug("envPath = %s", envPath)
256	LogDebug("usrPath = %s", usrPath)
257	LogDebug("path = %s", path)
258	return
259}
260
261func handleExecutable(ea ExtractionArgs) (success bool) {
262	// get the list of bitcode paths
263	var artifactPaths []string
264	artifactPaths, success = ea.Extractor(ea.InputFile)
265	if !success && ea.StrictExtract {
266		return
267	}
268
269	if len(artifactPaths) < 20 {
270		// naert: to avoid saturating the log when dealing with big file lists
271		LogInfo("handleExecutable: artifactPaths = %v\n", artifactPaths)
272	}
273
274	if len(artifactPaths) == 0 {
275		return
276	}
277	filesToLink := make([]string, len(artifactPaths))
278	for i, artPath := range artifactPaths {
279		filesToLink[i] = resolveBitcodePath(artPath)
280	}
281
282	// Sort the bitcode files
283	if ea.SortBitcodeFiles {
284		LogWarning("Sorting bitcode files.")
285		sort.Strings(filesToLink)
286		sort.Strings(artifactPaths)
287	}
288
289	// Write manifest
290	if ea.WriteManifest {
291		if !writeManifest(ea, filesToLink, artifactPaths) {
292			return
293		}
294	}
295
296	success = linkBitcodeFiles(ea, filesToLink)
297	return
298}
299
300func handleThinArchive(ea ExtractionArgs) (success bool) {
301	// List bitcode files to link
302	var artifactFiles []string
303
304	var objectFiles []string
305	var bcFiles []string
306
307	objectFiles = listArchiveFiles(ea, ea.InputFile)
308
309	LogInfo("handleThinArchive: ExtractionArgs = %v\nobjectFiles = %v\n", ea, objectFiles)
310
311	for index, obj := range objectFiles {
312		LogInfo("obj = '%v'\n", obj)
313		if len(obj) > 0 {
314			var artifacts []string
315			artifacts, success = ea.Extractor(obj)
316			if !success && ea.StrictExtract {
317				return
318			}
319			LogInfo("\t%v\n", artifacts)
320			artifactFiles = append(artifactFiles, artifacts...)
321			for _, bc := range artifacts {
322				bcPath := resolveBitcodePath(bc)
323				if bcPath != "" {
324					bcFiles = append(bcFiles, bcPath)
325				}
326			}
327		} else {
328			LogDebug("\tskipping empty entry at index %v\n", index)
329		}
330	}
331
332	LogInfo("bcFiles: %v\n", bcFiles)
333	LogInfo("len(bcFiles) = %v\n", len(bcFiles))
334
335	if len(bcFiles) > 0 {
336
337		// Sort the bitcode files
338		if ea.SortBitcodeFiles {
339			LogWarning("Sorting bitcode files.")
340			sort.Strings(bcFiles)
341			sort.Strings(artifactFiles)
342		}
343
344		// Build archive
345		if ea.BuildBitcodeModule {
346			success = linkBitcodeFiles(ea, bcFiles)
347		} else {
348			success = archiveBcFiles(ea, bcFiles)
349		}
350
351		if !success {
352			return
353		}
354
355		// Write manifest
356		if ea.WriteManifest {
357			success = writeManifest(ea, bcFiles, artifactFiles)
358		}
359	} else {
360		LogError("No bitcode files found\n")
361		success = false
362	}
363	return
364}
365
366func listArchiveFiles(ea ExtractionArgs, inputFile string) (contents []string) {
367	var arArgs []string
368	arArgs = append(arArgs, "-t")
369	arArgs = append(arArgs, inputFile)
370	output, err := runCmd(ea.ArchiverName, arArgs)
371	if err != nil {
372		LogWarning("ar command: %v %v", ea.ArchiverName, arArgs)
373		LogError("Failed to extract contents from archive %s because: %v.\n", inputFile, err)
374		return
375	}
376	contents = strings.Split(output, "\n")
377	return
378}
379
380func extractFile(ea ExtractionArgs, archive string, filename string, instance int) (success bool) {
381	var arArgs []string
382	if runtime.GOOS != osDARWIN {
383		arArgs = append(arArgs, "xN")
384		arArgs = append(arArgs, strconv.Itoa(instance))
385	} else {
386		if instance > 1 {
387			LogWarning("Cannot extract instance %v of %v from archive %s for instance > 1.\n", instance, filename, archive)
388			return
389		}
390		arArgs = append(arArgs, "x")
391	}
392	arArgs = append(arArgs, archive)
393	arArgs = append(arArgs, filename)
394	_, err := runCmd(ea.ArchiverName, arArgs)
395	if err != nil {
396		LogWarning("The archiver %v failed to extract instance %v of %v from archive %s because: %v.\n", ea.ArchiverName, instance, filename, archive, err)
397		return
398	}
399	success = true
400	return
401}
402
403func fetchTOC(ea ExtractionArgs, inputFile string) map[string]int {
404	toc := make(map[string]int)
405
406	contents := listArchiveFiles(ea, inputFile)
407
408	for _, item := range contents {
409		//iam: this is a hack to make get-bc work on libcurl.a
410		if item != "" && !strings.HasPrefix(item, "__.SYMDEF") {
411			toc[item]++
412		}
413	}
414	return toc
415}
416
417func extractFiles(ea ExtractionArgs, inputFile string, toc map[string]int) (success bool, artifactFiles []string, bcFiles []string) {
418	for obj, instance := range toc {
419		for i := 1; i <= instance; i++ {
420			if obj != "" && extractFile(ea, inputFile, obj, i) {
421				var artifacts []string
422				artifacts, success = ea.Extractor(obj)
423				if !success && ea.StrictExtract {
424					LogError("Failed to extract obj = %v occurrence = %v from %v", obj, i, inputFile)
425					return
426				}
427				LogInfo("\t%v\n", artifacts)
428				artifactFiles = append(artifactFiles, artifacts...)
429				for _, bc := range artifacts {
430					bcPath := resolveBitcodePath(bc)
431					if bcPath != "" {
432						bcFiles = append(bcFiles, bcPath)
433					}
434				}
435			}
436		}
437	}
438	// indicate overall success (we have already failed if using strict extract)
439	success = true
440	return
441}
442
443//handleArchive processes an archive, and creates either a bitcode archive, or a module, depending on the flags used.
444//
445//    Archives are strange beasts. handleArchive processes the archive by:
446//
447//      1. first creating a table of contents of the archive, which maps file names (in the archive) to the number of
448//    times a file with that name is stored in the archive.
449//
450//      2. for each OCCURRENCE of a file (name and count) it extracts the section from the object file, and adds the
451//    bitcode paths to the bitcode list.
452//
453//      3. it then either links all these bitcode files together using llvm-link,  or else is creates a bitcode
454//    archive using llvm-ar
455//
456//iam: 5/1/2018
457func handleArchive(ea ExtractionArgs) (success bool) {
458	// List bitcode files to link
459	var bcFiles []string
460	var artifactFiles []string
461
462	inputFile, _ := filepath.Abs(ea.InputFile)
463
464	LogInfo("handleArchive: ExtractionArgs = %v\n", ea)
465
466	// Create tmp dir
467	tmpDirName, err := ioutil.TempDir("", "gllvm")
468	if err != nil {
469		LogError("The temporary directory in which to extract object files could not be created.")
470		return
471	}
472
473	defer CheckDefer(func() error { return os.RemoveAll(tmpDirName) })
474
475	homeDir, err := os.Getwd()
476	if err != nil {
477		LogError("Could not ascertain our whereabouts: %v", err)
478		return
479	}
480
481	err = os.Chdir(tmpDirName)
482	if err != nil {
483		LogError("Could not cd to %v because: %v", tmpDirName, err)
484		return
485	}
486
487	//1. fetch the Table of Contents (TOC)
488	toc := fetchTOC(ea, inputFile)
489
490	LogDebug("Table of Contents of %v:\n%v\n", inputFile, toc)
491
492	//2. extract the files from the TOC
493	success, artifactFiles, bcFiles = extractFiles(ea, inputFile, toc)
494	//extractFiles has already complained
495	if !success {
496		return
497	}
498
499	err = os.Chdir(homeDir)
500	if err != nil {
501		LogError("Could not cd to %v because: %v", homeDir, err)
502		return
503	}
504
505	LogDebug("handleArchive: walked %v\nartifactFiles:\n%v\nbcFiles:\n%v\n", tmpDirName, artifactFiles, bcFiles)
506
507	//3. link or archive those puppies
508	if len(bcFiles) > 0 {
509
510		// Sort the bitcode files
511		if ea.SortBitcodeFiles {
512			LogWarning("Sorting bitcode files.")
513			sort.Strings(bcFiles)
514			sort.Strings(artifactFiles)
515		}
516
517		// Build archive
518		if ea.BuildBitcodeModule {
519			success = linkBitcodeFiles(ea, bcFiles)
520		} else {
521			success = archiveBcFiles(ea, bcFiles)
522		}
523
524		if !success {
525			//hopefully the failure has already been reported...
526			return
527		}
528
529		// Write manifest
530		if ea.WriteManifest {
531			success = writeManifest(ea, bcFiles, artifactFiles)
532		}
533	} else {
534		LogError("No bitcode files found\n")
535		return
536	}
537	return
538}
539
540func archiveBcFiles(ea ExtractionArgs, bcFiles []string) (success bool) {
541	// We do not want full paths in the archive, so we need to chdir into each
542	// bitcode's folder. Handle this by calling llvm-ar once for all bitcode
543	// files in the same directory
544	dirToBcMap := make(map[string][]string)
545	for _, bcFile := range bcFiles {
546		dirName, baseName := path.Split(bcFile)
547		dirToBcMap[dirName] = append(dirToBcMap[dirName], baseName)
548	}
549
550	// Call llvm-ar from each directory
551	absOutputFile, _ := filepath.Abs(ea.OutputFile)
552	for dir, bcFilesInDir := range dirToBcMap {
553		var args []string
554		var err error
555		args = append(args, "rs", absOutputFile)
556		args = append(args, bcFilesInDir...)
557		success, err = execCmd(ea.LlvmArchiverName, args, dir)
558		LogInfo("ea.LlvmArchiverName = %s, args = %v, dir = %s\n", ea.LlvmArchiverName, args, dir)
559		if !success {
560			LogError("There was an error creating the bitcode archive: %v.\n", err)
561			return
562		}
563	}
564	informUser("Built bitcode archive: %s.\n", ea.OutputFile)
565	success = true
566	return
567}
568
569func getsize(stringslice []string) (totalLength int) {
570	totalLength = 0
571	for _, s := range stringslice {
572		totalLength += len(s)
573	}
574	return totalLength
575}
576
577func formatStdOut(stdout bytes.Buffer, usefulIndex int) string {
578	infoArr := strings.Split(stdout.String(), "\n")[usefulIndex]
579	ret := strings.Fields(infoArr)
580	return ret[0]
581}
582
583func fetchArgMax(ea ExtractionArgs) (argMax int) {
584	if ea.LinkArgSize == 0 {
585		getArgMax := exec.Command("getconf", "ARG_MAX")
586		var argMaxStr bytes.Buffer
587		getArgMax.Stdout = &argMaxStr
588		err := getArgMax.Run()
589		if err != nil {
590			LogError("getconf ARG_MAX failed with %s\n", err)
591		}
592		argMax, err = strconv.Atoi(formatStdOut(argMaxStr, 0))
593		if err != nil {
594			LogError("string conversion for argMax failed with %s\n", err)
595		}
596		argMax = int(0.9 * float32(argMax)) // keeping a comfort margin
597	} else {
598		argMax = ea.LinkArgSize
599	}
600	LogInfo("argMax = %v\n", argMax)
601	return
602}
603
604func linkBitcodeFilesIncrementally(ea ExtractionArgs, filesToLink []string, argMax int, linkArgs []string) (success bool) {
605	var tmpFileList []string
606	// Create tmp dir
607	tmpDirName, err := ioutil.TempDir(".", "glinking")
608	if err != nil {
609		LogError("The temporary directory in which to put temporary linking files could not be created.")
610		return
611	}
612	if !ea.KeepTemp { // delete temporary folder after used unless told otherwise
613		LogInfo("Temporary folder will be deleted")
614		defer CheckDefer(func() error { return os.RemoveAll(tmpDirName) })
615	} else {
616		LogInfo("Keeping the temporary folder")
617	}
618
619	tmpFile, err := ioutil.TempFile(tmpDirName, "tmp")
620	if err != nil {
621		LogError("The temporary linking file could not be created.")
622		return
623	}
624	tmpFileList = append(tmpFileList, tmpFile.Name())
625	linkArgs = append(linkArgs, "-o", tmpFile.Name())
626
627	LogInfo("llvm-link argument size : %d", getsize(filesToLink))
628	for _, file := range filesToLink {
629		linkArgs = append(linkArgs, file)
630		if getsize(linkArgs) > argMax {
631			LogInfo("Linking command size exceeding system capacity : splitting the command")
632			success, err = execCmd(ea.LlvmLinkerName, linkArgs, "")
633			if !success || err != nil {
634				LogError("There was an error linking input files into %s because %v, on file %s.\n", ea.OutputFile, err, file)
635				success = false
636				return
637			}
638			linkArgs = nil
639
640			if ea.Verbose {
641				linkArgs = append(linkArgs, "-v")
642			}
643			tmpFile, err = ioutil.TempFile(tmpDirName, "tmp")
644			if err != nil {
645				LogError("Could not generate a temp file in %s because %v.\n", tmpDirName, err)
646				success = false
647				return
648			}
649			tmpFileList = append(tmpFileList, tmpFile.Name())
650			linkArgs = append(linkArgs, "-o", tmpFile.Name())
651		}
652
653	}
654	success, err = execCmd(ea.LlvmLinkerName, linkArgs, "")
655	if !success || err != nil {
656		LogError("There was an error linking input files into %s because %v.\n", tmpFile.Name(), err)
657		success = false
658		return
659	}
660	linkArgs = nil
661	if ea.Verbose {
662		linkArgs = append(linkArgs, "-v")
663	}
664	linkArgs = append(linkArgs, tmpFileList...)
665
666	linkArgs = append(linkArgs, "-o", ea.OutputFile)
667
668	success, err = execCmd(ea.LlvmLinkerName, linkArgs, "")
669	if !success {
670		LogError("There was an error linking input files into %s because %v.\n", ea.OutputFile, err)
671		return
672	}
673	LogInfo("Bitcode file extracted to: %s, from files %v \n", ea.OutputFile, tmpFileList)
674	success = true
675	return
676}
677
678func linkBitcodeFiles(ea ExtractionArgs, filesToLink []string) (success bool) {
679	var linkArgs []string
680	// Extracting the command line max size from the environment if it is not specified
681	argMax := fetchArgMax(ea)
682	if ea.Verbose {
683		linkArgs = append(linkArgs, "-v")
684	}
685	if getsize(filesToLink) > argMax { //command line size too large for the OS (necessitated by chromium)
686		return linkBitcodeFilesIncrementally(ea, filesToLink, argMax, linkArgs)
687	}
688	var err error
689	linkArgs = append(linkArgs, "-o", ea.OutputFile)
690	linkArgs = append(linkArgs, filesToLink...)
691	success, err = execCmd(ea.LlvmLinkerName, linkArgs, "")
692	if !success {
693		LogError("There was an error linking input files into %s because %v.\n", ea.OutputFile, err)
694		return
695	}
696	informUser("Bitcode file extracted to: %s.\n", ea.OutputFile)
697	success = true
698	return
699}
700
701func extractSectionDarwin(inputFile string) (contents []string, success bool) {
702	machoFile, err := macho.Open(inputFile)
703	if err != nil {
704		LogError("Mach-O file %s could not be read.", inputFile)
705		return
706	}
707	section := machoFile.Section(DarwinSectionName)
708	if section == nil {
709		LogError("The %s section of %s is missing!\n", DarwinSectionName, inputFile)
710		return
711	}
712	sectionContents, errContents := section.Data()
713	if errContents != nil {
714		LogError("Error reading the %s section of Mach-O file %s.", DarwinSectionName, inputFile)
715		return
716	}
717	contents = strings.Split(strings.TrimSuffix(string(sectionContents), "\n"), "\n")
718	success = true
719	return
720}
721
722func extractSectionUnix(inputFile string) (contents []string, success bool) {
723	elfFile, err := elf.Open(inputFile)
724	if err != nil {
725		LogError("ELF file %s could not be read.", inputFile)
726		return
727	}
728	section := elfFile.Section(ELFSectionName)
729	if section == nil {
730		LogError("Error reading the %s section of ELF file %s.", ELFSectionName, inputFile)
731		return
732	}
733	sectionContents, errContents := section.Data()
734	if errContents != nil {
735		LogError("Error reading the %s section of ELF file %s.", ELFSectionName, inputFile)
736		return
737	}
738	contents = strings.Split(strings.TrimSuffix(string(sectionContents), "\n"), "\n")
739	success = true
740	return
741}
742
743// Return the actual path to the bitcode file, or an empty string if it does not exist
744func resolveBitcodePath(bcPath string) string {
745	if _, err := os.Stat(bcPath); os.IsNotExist(err) {
746		// If the bitcode file does not exist, try to find it in the store
747		if LLVMBitcodeStorePath != "" {
748			// Compute absolute path hash
749			absBcPath, _ := filepath.Abs(bcPath)
750			storeBcPath := path.Join(LLVMBitcodeStorePath, getHashedPath(absBcPath))
751			if _, err := os.Stat(storeBcPath); os.IsNotExist(err) {
752				return ""
753			}
754			return storeBcPath
755		}
756		LogWarning("Failed to find the file %v\n", bcPath)
757		return ""
758	}
759	return bcPath
760}
761
762func writeManifest(ea ExtractionArgs, bcFiles []string, artifactFiles []string) (success bool) {
763	manifestFilename := ea.OutputFile + ".llvm.manifest"
764	//only go into the gory details if we have a store around.
765	if LLVMBitcodeStorePath != "" {
766		section1 := "Physical location of extracted files:\n" + strings.Join(bcFiles, "\n") + "\n\n"
767		section2 := "Build-time location of extracted files:\n" + strings.Join(artifactFiles, "\n")
768		contents := []byte(section1 + section2)
769		if err := ioutil.WriteFile(manifestFilename, contents, 0644); err != nil {
770			LogError("There was an error while writing the manifest file: ", err)
771			return
772		}
773	} else {
774		contents := []byte("\n" + strings.Join(bcFiles, "\n") + "\n")
775		if err := ioutil.WriteFile(manifestFilename, contents, 0644); err != nil {
776			LogError("There was an error while writing the manifest file: ", err)
777			return
778		}
779	}
780	informUser("Manifest file written to %s.\n", manifestFilename)
781	success = true
782	return
783}
784