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