mirror of
https://github.com/danog/gllvm.git
synced 2024-11-26 21:14:48 +01:00
conflict fix.
This commit is contained in:
commit
8a6e56d02d
@ -4,7 +4,7 @@
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/SRI-CSL/gllvm)](https://goreportcard.com/report/github.com/SRI-CSL/gllvm)
|
||||
|
||||
**TL; DR:** A drop-in replacement for [wllvm](https://github.com/SRI-CSL/whole-program-llvm), that builds the
|
||||
bitcode in parallel, and is faster.
|
||||
bitcode in parallel, and is faster. A comparison between the two tools can be gleaned from building the [Linux kernel.](https://github.com/SRI-CSL/gllvm/tree/master/examples/linux-kernel)
|
||||
|
||||
## Quick Start Comparison Table
|
||||
|
||||
|
@ -39,14 +39,14 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
shared.LogInfo("Entering %v\n", os.Args)
|
||||
shared.LogInfo("Entering CC %v\n", os.Args[1:])
|
||||
// Parse command line
|
||||
args := os.Args
|
||||
args = args[1:]
|
||||
|
||||
exitCode := shared.Compile(args, "clang")
|
||||
|
||||
shared.LogInfo("Calling %v returned %v\n", os.Args, exitCode)
|
||||
shared.LogDebug("Calling %v returned %v\n", os.Args, exitCode)
|
||||
|
||||
//important to pretend to look like the actual wrapped command
|
||||
os.Exit(exitCode)
|
||||
|
8
examples/README.md
Normal file
8
examples/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Examples of using GLLVM
|
||||
|
||||
|
||||
A simple set of instructions for building apache in a vagrant Ubuntu 14.04 can be found
|
||||
[here,](tutorial.md) and for Ubuntu 16.04 [here.](tutorial-ubuntu-16.04.md)
|
||||
|
||||
The big example here though is the [linux kernel.](linux-kernel)
|
||||
|
118
examples/linux-kernel/README.md
Normal file
118
examples/linux-kernel/README.md
Normal file
@ -0,0 +1,118 @@
|
||||
# Building a recent Linux Kernel.
|
||||
|
||||
In this directory we include all the necessary files needed to
|
||||
build the kernel in a Ubuntu 16.04 vagrant box. We will guide the reader through
|
||||
the relatively simple task. We assume familiarity with [Vagrant.](https://www.vagrantup.com/)
|
||||
|
||||
## Vagrantfile
|
||||
|
||||
```ruby
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
|
||||
config.vm.box = "ubuntu/xenial64"
|
||||
config.vm.provision :shell, path: "bootstrap.sh"
|
||||
|
||||
config.vm.provider "virtualbox" do |vb|
|
||||
vb.memory = "4096"
|
||||
vb.customize ["modifyvm", :id, "--ioapic", "on"]
|
||||
vb.customize ["modifyvm", :id, "--memory", "4096"]
|
||||
vb.customize ["modifyvm", :id, "--cpus", "4"]
|
||||
end
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
## Bootstrapping
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
sudo apt-get update
|
||||
|
||||
sudo apt-get install -y emacs24 dbus-x11
|
||||
sudo apt-get install -y git
|
||||
sudo apt-get install -y llvm-5.0 libclang-5.0-dev clang-5.0
|
||||
sudo apt-get install -y python-pip golang-go
|
||||
sudo apt-get install -y flex bison bc libncurses5-dev
|
||||
sudo apt-get install -y libelf-dev libssl-dev
|
||||
|
||||
echo ". /vagrant/bash_profile" >> /home/vagrant/.bashrc
|
||||
```
|
||||
|
||||
## Shell Settings
|
||||
|
||||
```bash
|
||||
#### llvm
|
||||
export LLVM_HOME=/usr/lib/llvm-5.0
|
||||
export GOPATH=/vagrant/go
|
||||
|
||||
######## gllvm/wllvm configuration #############
|
||||
|
||||
export LLVM_COMPILER=clang
|
||||
export WLLVM_OUTPUT_LEVEL=WARNING
|
||||
export WLLVM_OUTPUT_FILE=/vagrant/wrapper.log
|
||||
export PATH=${GOPATH}/bin:${LLVM_HOME}/bin:${PATH}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Configuration stuff.
|
||||
|
||||
The file `tinyconfig64` is generated ...
|
||||
|
||||
## The Build with gllvm
|
||||
|
||||
The build process is carried out by running the `build_linux_gllvm.sh`
|
||||
script.
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
mkdir -p ${GOPATH}
|
||||
go get github.com/SRI-CSL/gllvm/cmd/...
|
||||
|
||||
mkdir ${HOME}/linux_kernel
|
||||
cd ${HOME}/linux_kernel
|
||||
git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
|
||||
|
||||
cd linux-stable
|
||||
git checkout tags/v4.14.34
|
||||
cp /vagrant/tinyconfig64 .config
|
||||
|
||||
make CC=gclang HOSTCC=gclang
|
||||
|
||||
get-bc -m -b built-in.o
|
||||
get-bc -m vmlinux
|
||||
```
|
||||
|
||||
## The Build with wllvm
|
||||
|
||||
The build process is carried out by running the `build_linux_wllvm.sh`
|
||||
script.
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
sudo pip install wllvm
|
||||
|
||||
mkdir ${HOME}/linux_kernel
|
||||
cd ${HOME}/linux_kernel
|
||||
git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
|
||||
|
||||
cd linux-stable
|
||||
git checkout tags/v4.14.34
|
||||
cp /vagrant/tinyconfig64 .config
|
||||
|
||||
|
||||
make CC=wllvm HOSTCC=wllvm
|
||||
|
||||
extract-bc -m -b built-in.o
|
||||
extract-bc -m vmlinux
|
||||
```
|
||||
|
||||
## Extracting the bitcode
|
||||
|
20
examples/linux-kernel/Vagrantfile
vendored
Normal file
20
examples/linux-kernel/Vagrantfile
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
|
||||
config.vm.box = "ubuntu/xenial64"
|
||||
config.vm.provision :shell, path: "bootstrap.sh"
|
||||
|
||||
config.vm.provider "virtualbox" do |vb|
|
||||
vb.memory = "4096"
|
||||
vb.customize ["modifyvm", :id, "--ioapic", "on"]
|
||||
vb.customize ["modifyvm", :id, "--memory", "4096"]
|
||||
vb.customize ["modifyvm", :id, "--cpus", "4"]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
10
examples/linux-kernel/bash_profile
Normal file
10
examples/linux-kernel/bash_profile
Normal file
@ -0,0 +1,10 @@
|
||||
#### llvm
|
||||
export LLVM_HOME=/usr/lib/llvm-5.0
|
||||
export GOPATH=/vagrant/go
|
||||
|
||||
######## gllvm/wllvm configuration #############
|
||||
|
||||
export LLVM_COMPILER=clang
|
||||
export WLLVM_OUTPUT_LEVEL=WARNING
|
||||
export WLLVM_OUTPUT_FILE=/vagrant/wrapper.log
|
||||
export PATH=${GOPATH}/bin:${LLVM_HOME}/bin:${PATH}
|
12
examples/linux-kernel/bootstrap.sh
Normal file
12
examples/linux-kernel/bootstrap.sh
Normal file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
sudo apt-get update
|
||||
|
||||
sudo apt-get install -y emacs24 dbus-x11
|
||||
sudo apt-get install -y git
|
||||
sudo apt-get install -y llvm-5.0 libclang-5.0-dev clang-5.0
|
||||
sudo apt-get install -y python-pip golang-go
|
||||
sudo apt-get install -y flex bison bc libncurses5-dev
|
||||
sudo apt-get install -y libelf-dev libssl-dev
|
||||
|
||||
echo ". /vagrant/bash_profile" >> /home/vagrant/.bashrc
|
17
examples/linux-kernel/build_linux_gllvm.sh
Executable file
17
examples/linux-kernel/build_linux_gllvm.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
mkdir -p ${GOPATH}
|
||||
go get github.com/SRI-CSL/gllvm/cmd/...
|
||||
|
||||
mkdir ${HOME}/linux_kernel
|
||||
cd ${HOME}/linux_kernel
|
||||
git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
|
||||
|
||||
cd linux-stable
|
||||
git checkout tags/v4.14.34
|
||||
cp /vagrant/tinyconfig64 .config
|
||||
|
||||
make CC=gclang HOSTCC=gclang
|
||||
|
||||
get-bc -m -b built-in.o
|
||||
get-bc -m vmlinux
|
17
examples/linux-kernel/build_linux_wllvm.sh
Executable file
17
examples/linux-kernel/build_linux_wllvm.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
sudo pip install wllvm
|
||||
|
||||
mkdir ${HOME}/linux_kernel
|
||||
cd ${HOME}/linux_kernel
|
||||
git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
|
||||
|
||||
cd linux-stable
|
||||
git checkout tags/v4.14.34
|
||||
cp /vagrant/tinyconfig64 .config
|
||||
|
||||
|
||||
make CC=wllvm HOSTCC=wllvm
|
||||
|
||||
extract-bc -m -b built-in.o
|
||||
extract-bc -m vmlinux
|
1048
examples/linux-kernel/tinyconfig64
Normal file
1048
examples/linux-kernel/tinyconfig64
Normal file
File diff suppressed because it is too large
Load Diff
86
examples/tutorial-ubuntu-16.04.md
Normal file
86
examples/tutorial-ubuntu-16.04.md
Normal file
@ -0,0 +1,86 @@
|
||||
# Compiling Apache on Ubuntu
|
||||
|
||||
|
||||
On a clean 16.04 server machine I will build apache. Desktop instructions should be no different.
|
||||
|
||||
```
|
||||
>more /etc/lsb-release
|
||||
|
||||
DISTRIB_ID=Ubuntu
|
||||
DISTRIB_RELEASE=16.04
|
||||
DISTRIB_CODENAME=xenial
|
||||
DISTRIB_DESCRIPTION="Ubuntu 16.04 LTS"
|
||||
```
|
||||
|
||||
|
||||
## Step 1.
|
||||
|
||||
Install `gllvm`.
|
||||
|
||||
```
|
||||
>export GOPATH=/vagrant/go
|
||||
|
||||
>mkdir -p ${GOPATH}
|
||||
|
||||
>go get github.com/SRI-CSL/gllvm/cmd/...
|
||||
|
||||
>export PATH=${GOPATH}/bin:${PATH}
|
||||
```
|
||||
|
||||
## Step 2.
|
||||
|
||||
I am only going to build apache, not apr, so I first install the prerequisites.
|
||||
|
||||
```
|
||||
>sudo apt-get install llvm libclang-dev clang libapr1-dev libaprutil1-dev libpcre3-dev make
|
||||
|
||||
```
|
||||
|
||||
At this point, you could check your clang version with `which clang` and `ls -l /usr/bin/clang`.
|
||||
It should be at least clang-3.8.
|
||||
|
||||
## Step 3.
|
||||
|
||||
Configure the gllvm tool to be relatively quiet:
|
||||
|
||||
```
|
||||
>export WLLVM_OUTPUT_LEVEL=WARNING
|
||||
>export WLLVM_OUTPUT_FILE=/vagrant/apache-build.log
|
||||
```
|
||||
|
||||
## Step 4.
|
||||
|
||||
Fetch apache, untar, configure, then build:
|
||||
|
||||
```
|
||||
|
||||
>wget https://archive.apache.org/dist/httpd/httpd-2.4.33.tar.gz
|
||||
|
||||
>tar xfz httpd-2.4.33.tar.gz
|
||||
|
||||
>cd httpd-2.4.33
|
||||
|
||||
>CC=gllvm ./configure
|
||||
|
||||
>make
|
||||
```
|
||||
|
||||
## Step 5.
|
||||
|
||||
Extract the bitcode.
|
||||
|
||||
```
|
||||
>get-bc httpd
|
||||
|
||||
>ls -la httpd.bc
|
||||
-rw-r--r-- 1 vagrant vagrant 1119584 Aug 4 20:02 httpd.bc
|
||||
```
|
||||
|
||||
## Step 6.
|
||||
|
||||
Turn the bitcode into a second executable binary. (optional -- just for fun and sanity checking)
|
||||
|
||||
```
|
||||
llc -filetype=obj httpd.bc
|
||||
gcc httpd.o -lpthread -lapr-1 -laprutil-1 -lpcre -o httpd.new
|
||||
```
|
82
examples/tutorial.md
Normal file
82
examples/tutorial.md
Normal file
@ -0,0 +1,82 @@
|
||||
# Compiling Apache on Ubuntu
|
||||
|
||||
|
||||
On a clean 14.04 machine I will build apache.
|
||||
|
||||
```
|
||||
>pwd
|
||||
|
||||
/vagrant
|
||||
|
||||
>more /etc/lsb-release
|
||||
|
||||
DISTRIB_ID=Ubuntu
|
||||
DISTRIB_RELEASE=14.04
|
||||
DISTRIB_CODENAME=trusty
|
||||
DISTRIB_DESCRIPTION="Ubuntu 14.04.2 LTS"
|
||||
```
|
||||
|
||||
|
||||
## Step 1.
|
||||
|
||||
|
||||
Install `gllvm`.
|
||||
|
||||
```
|
||||
>export GOPATH=/vagrant/go
|
||||
|
||||
>mkdir -p ${GOPATH}
|
||||
|
||||
>go get github.com/SRI-CSL/gllvm/cmd/...
|
||||
|
||||
>export PATH=${GOPATH}/bin:${PATH}
|
||||
```
|
||||
|
||||
## Step 2.
|
||||
|
||||
I am only going to build apache, not apr, so I first install the prerequisites.
|
||||
|
||||
```
|
||||
>sudo apt-get install llvm libclang-dev clang libapr1-dev libaprutil1-dev
|
||||
|
||||
``` Note `gclang` is agnostic with respect to llvm versions
|
||||
so feel free to install a more recent version if you
|
||||
wish. However, if you are going to use dragonegg the llvm version is
|
||||
tightly coupled to the gcc and plugin versions you are using.
|
||||
|
||||
|
||||
## Step 3.
|
||||
|
||||
Configure the gllvm tool to be relatively quiet:
|
||||
|
||||
```
|
||||
>export WLLVM_OUTPUT_LEVEL=WARNING
|
||||
>export WLLVM_OUTPUT_FILE=/vagrant/apache-build.log
|
||||
```
|
||||
|
||||
|
||||
## Step 4.
|
||||
|
||||
Fetch apache, untar, configure, then build:
|
||||
|
||||
```
|
||||
>wget https://archive.apache.org/dist/httpd/httpd-2.4.33.tar.gz
|
||||
|
||||
>tar xfz httpd-2.4.33.tar.gz
|
||||
|
||||
>cd httpd-2.4.33
|
||||
|
||||
>CC=gclang ./configure
|
||||
|
||||
>make
|
||||
```
|
||||
|
||||
## Step 5.
|
||||
|
||||
Extract the bitcode.
|
||||
|
||||
```
|
||||
>get-bc httpd
|
||||
|
||||
```
|
||||
|
@ -16,3 +16,5 @@ gofmt -s -w shared/*.go cmd/*/*.go
|
||||
For linting:
|
||||
|
||||
https://github.com/alecthomas/gometalinter
|
||||
|
||||
gometalinter.v2 ./...
|
||||
|
@ -241,7 +241,7 @@ func buildBitcodeFile(compilerExecName string, pr parserResult, srcFile string,
|
||||
// Tries to build object file
|
||||
func execCompile(compilerExecName string, pr parserResult, wg *sync.WaitGroup, ok *bool) {
|
||||
defer (*wg).Done()
|
||||
success, err := execCmd(compilerExecName, pr.InputList, "")
|
||||
success, _ := execCmd(compilerExecName, pr.InputList, "")
|
||||
if !success {
|
||||
LogError("Failed to compile using given arguments:\n%v %v\nexit status: %v\n", compilerExecName, pr.InputList, err)
|
||||
*ok = false
|
||||
|
@ -33,8 +33,18 @@
|
||||
|
||||
package shared
|
||||
|
||||
const gllvmVersion = "1.0.1"
|
||||
const gllvmReleaseDate = "April 118 2018"
|
||||
// Remember to tag the repo, and publish a release on GitHub.
|
||||
//
|
||||
// version history:
|
||||
//
|
||||
// 1.0.0
|
||||
// 1.0.1 various bug fixes
|
||||
// 1.0.2 April 28 2018 linux kernel work, sorting bitcode files, etc.
|
||||
// May 2 2018 handleArchives rewritten to handle multiple occurrences of files with the same name.
|
||||
// corresponds with wllvm 1.1.6
|
||||
//
|
||||
const gllvmVersion = "1.0.2"
|
||||
const gllvmReleaseDate = "May 2 2018"
|
||||
|
||||
const osDARWIN = "darwin"
|
||||
const osLINUX = "linux"
|
||||
|
@ -103,3 +103,13 @@ func init() {
|
||||
LLVMLoggingFile = os.Getenv(envfile)
|
||||
|
||||
}
|
||||
|
||||
func printEnvironment() {
|
||||
vars := []string{envpath, envcc, envcxx, envar, envlnk, envcfg, envbc, envlvl, envfile}
|
||||
|
||||
LogWrite("\nLiving in this environment:\n\n")
|
||||
for _, v := range vars {
|
||||
LogWrite("%v = %v\n", v, os.Getenv(v))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,6 +42,8 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -56,6 +58,7 @@ type extractionArgs struct {
|
||||
Extractor func(string) []string
|
||||
Verbose bool
|
||||
WriteManifest bool
|
||||
SortBitcodeFiles bool
|
||||
BuildBitcodeArchive bool
|
||||
}
|
||||
|
||||
@ -86,7 +89,7 @@ func Extract(args []string) {
|
||||
|
||||
// Create output filename if not given
|
||||
if ea.OutputFile == "" {
|
||||
if ea.InputType == fileTypeARCHIVE {
|
||||
if ea.InputType == fileTypeARCHIVE || ea.InputType == fileTypeTHINARCHIVE {
|
||||
var ext string
|
||||
if ea.BuildBitcodeArchive {
|
||||
ext = ".a.bc"
|
||||
@ -109,6 +112,8 @@ func Extract(args []string) {
|
||||
handleExecutable(ea)
|
||||
case fileTypeARCHIVE:
|
||||
handleArchive(ea)
|
||||
case fileTypeTHINARCHIVE:
|
||||
handleThinArchive(ea)
|
||||
default:
|
||||
LogFatal("Incorrect input file type %v.", ea.InputType)
|
||||
}
|
||||
@ -125,6 +130,8 @@ func parseSwitches() (ea extractionArgs) {
|
||||
|
||||
writeManifestPtr := flag.Bool("m", false, "write the manifest")
|
||||
|
||||
sortBitcodeFilesPtr := flag.Bool("s", false, "sort the bitcode files")
|
||||
|
||||
buildBitcodeArchive := flag.Bool("b", false, "build a bitcode module(FIXME? should this be archive)")
|
||||
|
||||
outputFilePtr := flag.String("o", "", "the output file")
|
||||
@ -137,6 +144,7 @@ func parseSwitches() (ea extractionArgs) {
|
||||
|
||||
ea.Verbose = *verbosePtr
|
||||
ea.WriteManifest = *writeManifestPtr
|
||||
ea.SortBitcodeFiles = *sortBitcodeFilesPtr
|
||||
ea.BuildBitcodeArchive = *buildBitcodeArchive
|
||||
|
||||
if *archiverNamePtr != "" {
|
||||
@ -201,20 +209,141 @@ func handleExecutable(ea extractionArgs) {
|
||||
for i, artPath := range artifactPaths {
|
||||
filesToLink[i] = resolveBitcodePath(artPath)
|
||||
}
|
||||
extractTimeLinkFiles(ea, filesToLink)
|
||||
|
||||
// Sort the bitcode files
|
||||
if ea.SortBitcodeFiles {
|
||||
LogWarning("Sorting bitcode files.")
|
||||
sort.Strings(filesToLink)
|
||||
sort.Strings(artifactPaths)
|
||||
}
|
||||
|
||||
// Write manifest
|
||||
if ea.WriteManifest {
|
||||
writeManifest(ea, filesToLink, artifactPaths)
|
||||
}
|
||||
|
||||
extractTimeLinkFiles(ea, filesToLink)
|
||||
}
|
||||
|
||||
func handleThinArchive(ea extractionArgs) {
|
||||
// List bitcode files to link
|
||||
var artifactFiles []string
|
||||
|
||||
var objectFiles []string
|
||||
var bcFiles []string
|
||||
|
||||
objectFiles = listArchiveFiles(ea.InputFile)
|
||||
|
||||
LogInfo("handleThinArchive: extractionArgs = %v\nobjectFiles = %v\n", ea, objectFiles)
|
||||
|
||||
for index, obj := range objectFiles {
|
||||
LogInfo("obj = '%v'\n", obj)
|
||||
if len(obj) > 0 {
|
||||
artifacts := ea.Extractor(obj)
|
||||
LogInfo("\t%v\n", artifacts)
|
||||
artifactFiles = append(artifactFiles, artifacts...)
|
||||
for _, bc := range artifacts {
|
||||
bcPath := resolveBitcodePath(bc)
|
||||
if bcPath != "" {
|
||||
bcFiles = append(bcFiles, bcPath)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LogDebug("\tskipping empty entry at index %v\n", index)
|
||||
}
|
||||
}
|
||||
|
||||
LogInfo("bcFiles: %v\n", bcFiles)
|
||||
LogInfo("len(bcFiles) = %v\n", len(bcFiles))
|
||||
|
||||
if len(bcFiles) > 0 {
|
||||
|
||||
// Sort the bitcode files
|
||||
if ea.SortBitcodeFiles {
|
||||
LogWarning("Sorting bitcode files.")
|
||||
sort.Strings(bcFiles)
|
||||
sort.Strings(artifactFiles)
|
||||
}
|
||||
|
||||
// Build archive
|
||||
if ea.BuildBitcodeArchive {
|
||||
extractTimeLinkFiles(ea, bcFiles)
|
||||
} else {
|
||||
archiveBcFiles(ea, bcFiles)
|
||||
}
|
||||
|
||||
// Write manifest
|
||||
if ea.WriteManifest {
|
||||
writeManifest(ea, bcFiles, artifactFiles)
|
||||
}
|
||||
} else {
|
||||
LogError("No bitcode files found\n")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func listArchiveFiles(inputFile string) (contents []string) {
|
||||
var arArgs []string
|
||||
arArgs = append(arArgs, "-t")
|
||||
arArgs = append(arArgs, inputFile)
|
||||
output, err := runCmd("ar", arArgs)
|
||||
if err != nil {
|
||||
LogWarning("ar command: ar %v", arArgs)
|
||||
LogFatal("Failed to extract contents from archive %s because: %v.\n", inputFile, err)
|
||||
}
|
||||
contents = strings.Split(output, "\n")
|
||||
return
|
||||
}
|
||||
|
||||
func extractFile(archive string, filename string, instance int) bool {
|
||||
var arArgs []string
|
||||
arArgs = append(arArgs, "xN")
|
||||
arArgs = append(arArgs, strconv.Itoa(instance))
|
||||
arArgs = append(arArgs, archive)
|
||||
arArgs = append(arArgs, filename)
|
||||
_, err := runCmd("ar", arArgs)
|
||||
if err != nil {
|
||||
LogWarning("Failed to extract instance %v of %v from archive %s because: %v.\n", instance, filename, archive, err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func fetchTOC(inputFile string) map[string]int {
|
||||
toc := make(map[string]int)
|
||||
|
||||
contents := listArchiveFiles(inputFile)
|
||||
|
||||
for _, item := range contents {
|
||||
if item != "" {
|
||||
toc[item]++
|
||||
}
|
||||
}
|
||||
return toc
|
||||
}
|
||||
|
||||
//handleArchive processes a archive, and creates either a bitcode archive, or a module, depending on the flags used.
|
||||
//
|
||||
// Archives are strange beasts. handleArchive processes the archive by:
|
||||
//
|
||||
// 1. first creating a table of contents of the archive, which maps file names (in the archive) to the number of
|
||||
// times a file with that name is stored in the archive.
|
||||
//
|
||||
// 2. for each OCCURRENCE of a file (name and count) it extracts the section from the object file, and adds the
|
||||
// bitcode paths to the bitcode list.
|
||||
//
|
||||
// 3. it then either links all these bitcode files together using llvm-link, or else is creates a bitcode
|
||||
// archive using llvm-ar
|
||||
//
|
||||
//iam: 5/1/2018
|
||||
func handleArchive(ea extractionArgs) {
|
||||
// List bitcode files to link
|
||||
var bcFiles []string
|
||||
var artifactFiles []string
|
||||
|
||||
LogInfo("handleArchive: extractionArgs = %v\n", ea)
|
||||
inputFile, _ := filepath.Abs(ea.InputFile)
|
||||
|
||||
LogWarning("handleArchive: extractionArgs = %v\n", ea)
|
||||
|
||||
// Create tmp dir
|
||||
tmpDirName, err := ioutil.TempDir("", "gllvm")
|
||||
@ -223,45 +352,55 @@ func handleArchive(ea extractionArgs) {
|
||||
}
|
||||
defer CheckDefer(func() error { return os.RemoveAll(tmpDirName) })
|
||||
|
||||
// Extract objects to tmpDir
|
||||
arArgs := ea.ArArgs
|
||||
inputAbsPath, _ := filepath.Abs(ea.InputFile)
|
||||
arArgs = append(arArgs, inputAbsPath)
|
||||
|
||||
LogInfo("handleArchive: executing ar %v %v\n", arArgs, tmpDirName)
|
||||
|
||||
success, err := execCmd("ar", arArgs, tmpDirName)
|
||||
if !success {
|
||||
LogFatal("Failed to extract object files from %s to %s because: %v.\n", ea.InputFile, tmpDirName, err)
|
||||
homeDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
LogFatal("Could not ascertain our whereabouts: %v", err)
|
||||
}
|
||||
|
||||
// Define object file handling closure
|
||||
var walkHandlingFunc = func(path string, info os.FileInfo, err error) error {
|
||||
if err == nil && !info.IsDir() {
|
||||
fileType := getFileType(path)
|
||||
if fileType == ea.ObjectTypeInArchive {
|
||||
artifactPaths := ea.Extractor(path)
|
||||
for _, artPath := range artifactPaths {
|
||||
bcPath := resolveBitcodePath(artPath)
|
||||
err = os.Chdir(tmpDirName)
|
||||
if err != nil {
|
||||
LogFatal("Could not cd to %v because: %v", tmpDirName, err)
|
||||
}
|
||||
|
||||
//1. fetch the Table of Contents
|
||||
toc := fetchTOC(inputFile)
|
||||
|
||||
LogDebug("Table of Contents of %v:\n%v\n", inputFile, toc)
|
||||
|
||||
for obj, instance := range toc {
|
||||
for i := 1; i <= instance; i++ {
|
||||
|
||||
if obj != "" && extractFile(inputFile, obj, i) {
|
||||
|
||||
artifacts := ea.Extractor(obj)
|
||||
LogInfo("\t%v\n", artifacts)
|
||||
artifactFiles = append(artifactFiles, artifacts...)
|
||||
for _, bc := range artifacts {
|
||||
bcPath := resolveBitcodePath(bc)
|
||||
if bcPath != "" {
|
||||
bcFiles = append(bcFiles, bcPath)
|
||||
}
|
||||
}
|
||||
artifactFiles = append(artifactFiles, artifactPaths...)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle object files
|
||||
err = filepath.Walk(tmpDirName, walkHandlingFunc)
|
||||
err = os.Chdir(homeDir)
|
||||
if err != nil {
|
||||
LogFatal("handleArchive: walking %v failed with %v\n", tmpDirName, err)
|
||||
LogFatal("Could not cd to %v because: %v", homeDir, err)
|
||||
}
|
||||
|
||||
LogDebug("handleArchive: walked %v\nartifactFiles:\n%v\nbcFiles:\n%v\n", tmpDirName, artifactFiles, bcFiles)
|
||||
|
||||
if len(bcFiles) > 0 {
|
||||
|
||||
// Sort the bitcode files
|
||||
if ea.SortBitcodeFiles {
|
||||
LogWarning("Sorting bitcode files.")
|
||||
sort.Strings(bcFiles)
|
||||
sort.Strings(artifactFiles)
|
||||
}
|
||||
|
||||
// Build archive
|
||||
if ea.BuildBitcodeArchive {
|
||||
extractTimeLinkFiles(ea, bcFiles)
|
||||
@ -300,7 +439,7 @@ func archiveBcFiles(ea extractionArgs, bcFiles []string) {
|
||||
LogFatal("There was an error creating the bitcode archive: %v.\n", err)
|
||||
}
|
||||
}
|
||||
LogInfo("Built bitcode archive: %s.", ea.OutputFile)
|
||||
LogWarning("Built bitcode archive: %s.", ea.OutputFile)
|
||||
}
|
||||
|
||||
func extractTimeLinkFiles(ea extractionArgs, filesToLink []string) {
|
||||
@ -314,7 +453,7 @@ func extractTimeLinkFiles(ea extractionArgs, filesToLink []string) {
|
||||
if !success {
|
||||
LogFatal("There was an error linking input files into %s because %v.\n", ea.OutputFile, err)
|
||||
}
|
||||
LogInfo("Bitcode file extracted to: %s.", ea.OutputFile)
|
||||
LogWarning("Bitcode file extracted to: %s.", ea.OutputFile)
|
||||
}
|
||||
|
||||
func extractSectionDarwin(inputFile string) (contents []string) {
|
||||
@ -375,12 +514,20 @@ func resolveBitcodePath(bcPath string) string {
|
||||
}
|
||||
|
||||
func writeManifest(ea extractionArgs, bcFiles []string, artifactFiles []string) {
|
||||
section1 := "Physical location of extracted files:\n" + strings.Join(bcFiles, "\n") + "\n\n"
|
||||
section2 := "Build-time location of extracted files:\n" + strings.Join(artifactFiles, "\n")
|
||||
contents := []byte(section1 + section2)
|
||||
manifestFilename := ea.OutputFile + ".llvm.manifest"
|
||||
if err := ioutil.WriteFile(manifestFilename, contents, 0644); err != nil {
|
||||
LogFatal("There was an error while writing the manifest file: ", err)
|
||||
//only go into the gory details if we have a store around.
|
||||
if LLVMBitcodeStorePath != "" {
|
||||
section1 := "Physical location of extracted files:\n" + strings.Join(bcFiles, "\n") + "\n\n"
|
||||
section2 := "Build-time location of extracted files:\n" + strings.Join(artifactFiles, "\n")
|
||||
contents := []byte(section1 + section2)
|
||||
if err := ioutil.WriteFile(manifestFilename, contents, 0644); err != nil {
|
||||
LogFatal("There was an error while writing the manifest file: ", err)
|
||||
}
|
||||
} else {
|
||||
contents := []byte("\n" + strings.Join(bcFiles, "\n") + "\n")
|
||||
if err := ioutil.WriteFile(manifestFilename, contents, 0644); err != nil {
|
||||
LogFatal("There was an error while writing the manifest file: ", err)
|
||||
}
|
||||
}
|
||||
LogInfo("Manifest file written to %s.", manifestFilename)
|
||||
LogWarning("Manifest file written to %s.", manifestFilename)
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ const (
|
||||
fileTypeMACHOBJECT
|
||||
fileTypeMACHSHARED
|
||||
fileTypeARCHIVE
|
||||
fileTypeTHINARCHIVE
|
||||
)
|
||||
|
||||
func getFileType(realPath string) (fileType int) {
|
||||
@ -58,21 +59,36 @@ func getFileType(realPath string) (fileType int) {
|
||||
LogFatal("There was an error getting the type of %s. Make sure that the 'file' command is installed.", realPath)
|
||||
}
|
||||
|
||||
// Test the output
|
||||
if fo := string(out); strings.Contains(fo, "ELF") && strings.Contains(fo, "executable") {
|
||||
fileType = fileTypeELFEXECUTABLE
|
||||
} else if strings.Contains(fo, "Mach-O") && strings.Contains(fo, "executable") {
|
||||
fileType = fileTypeMACHEXECUTABLE
|
||||
} else if strings.Contains(fo, "ELF") && strings.Contains(fo, "shared") {
|
||||
fileType = fileTypeELFSHARED
|
||||
} else if strings.Contains(fo, "Mach-O") && strings.Contains(fo, "dynamically linked shared") {
|
||||
fileType = fileTypeMACHSHARED
|
||||
fo := string(out)
|
||||
|
||||
if strings.Contains(fo, "ELF") {
|
||||
|
||||
if strings.Contains(fo, "executable") {
|
||||
fileType = fileTypeELFEXECUTABLE
|
||||
} else if strings.Contains(fo, "shared") {
|
||||
fileType = fileTypeELFSHARED
|
||||
} else if strings.Contains(fo, "relocatable") {
|
||||
fileType = fileTypeELFOBJECT
|
||||
} else {
|
||||
fileType = fileTypeUNDEFINED
|
||||
}
|
||||
|
||||
} else if strings.Contains(fo, "Mach-O") {
|
||||
|
||||
if strings.Contains(fo, "executable") {
|
||||
fileType = fileTypeMACHEXECUTABLE
|
||||
} else if strings.Contains(fo, "dynamically linked shared") {
|
||||
fileType = fileTypeMACHSHARED
|
||||
} else if strings.Contains(fo, "object") {
|
||||
fileType = fileTypeMACHOBJECT
|
||||
} else {
|
||||
fileType = fileTypeUNDEFINED
|
||||
}
|
||||
|
||||
} else if strings.Contains(fo, "current ar archive") {
|
||||
fileType = fileTypeARCHIVE
|
||||
} else if strings.Contains(fo, "ELF") && strings.Contains(fo, "relocatable") {
|
||||
fileType = fileTypeELFOBJECT
|
||||
} else if strings.Contains(fo, "Mach-O") && strings.Contains(fo, "object") {
|
||||
fileType = fileTypeMACHOBJECT
|
||||
} else if strings.Contains(fo, "thin archive") {
|
||||
fileType = fileTypeTHINARCHIVE
|
||||
} else {
|
||||
fileType = fileTypeUNDEFINED
|
||||
}
|
||||
|
@ -55,14 +55,14 @@ var loggingLevels = map[string]int{
|
||||
}
|
||||
|
||||
var loggingPrefixes = map[int]string{
|
||||
errorV: "Error: ",
|
||||
warningV: "Warning: ",
|
||||
infoV: "Info: ",
|
||||
debugV: "Debug: ",
|
||||
errorV: "ERROR:",
|
||||
warningV: "WARNING:",
|
||||
infoV: "INFO:",
|
||||
debugV: "DEBUG:",
|
||||
}
|
||||
|
||||
//loggingLevel is the user configured level of logging: ERROR, WARNING, INFO, DEBUG
|
||||
var loggingLevel = errorV
|
||||
var loggingLevel = warningV
|
||||
|
||||
//loggingFilePointer is where the logging is streamed too.
|
||||
var loggingFilePointer = os.Stderr
|
||||
|
@ -88,6 +88,9 @@ func parse(argList []string) parserResult {
|
||||
pr.InputList = argList
|
||||
|
||||
var argsExactMatches = map[string]flagInfo{
|
||||
|
||||
"/dev/null": {0, pr.inputFileCallback}, //iam: linux kernel
|
||||
|
||||
"-": {0, pr.printOnlyCallback},
|
||||
"-o": {1, pr.outputFileCallback},
|
||||
"-c": {0, pr.compileOnlyCallback},
|
||||
@ -118,41 +121,50 @@ func parse(argList []string) parserResult {
|
||||
"-nostdlibinc": {0, pr.compileUnaryCallback},
|
||||
|
||||
"-mno-omit-leaf-frame-pointer": {0, pr.compileUnaryCallback},
|
||||
"-maes": {0, pr.compileUnaryCallback},
|
||||
"-mno-aes": {0, pr.compileUnaryCallback},
|
||||
"-mavx": {0, pr.compileUnaryCallback},
|
||||
"-mno-avx": {0, pr.compileUnaryCallback},
|
||||
"-mavx2": {0, pr.compileUnaryCallback},
|
||||
"-mno-avx2": {0, pr.compileUnaryCallback},
|
||||
"-mcmodel=kernel": {0, pr.compileUnaryCallback},
|
||||
"-mno-red-zone": {0, pr.compileUnaryCallback},
|
||||
"-mmmx": {0, pr.compileUnaryCallback},
|
||||
"-mno-mmx": {0, pr.compileUnaryCallback},
|
||||
"-msse": {0, pr.compileUnaryCallback},
|
||||
"-mno-sse": {0, pr.compileUnaryCallback},
|
||||
"-msse2": {0, pr.compileUnaryCallback},
|
||||
"-mno-sse2": {0, pr.compileUnaryCallback},
|
||||
"-msse3": {0, pr.compileUnaryCallback},
|
||||
"-mno-sse3": {0, pr.compileUnaryCallback},
|
||||
"-mssse3": {0, pr.compileUnaryCallback},
|
||||
"-mno-ssse3": {0, pr.compileUnaryCallback},
|
||||
"-msse4": {0, pr.compileUnaryCallback},
|
||||
"-mno-sse4": {0, pr.compileUnaryCallback},
|
||||
"-msse4.1": {0, pr.compileUnaryCallback},
|
||||
"-mno-sse4.1": {0, pr.compileUnaryCallback},
|
||||
"-msse4.2": {0, pr.compileUnaryCallback},
|
||||
"-mno-sse4.2": {0, pr.compileUnaryCallback},
|
||||
"-msoft-float": {0, pr.compileUnaryCallback},
|
||||
"-m3dnow": {0, pr.compileUnaryCallback},
|
||||
"-mno-3dnow": {0, pr.compileUnaryCallback},
|
||||
"-m32": {0, pr.compileUnaryCallback},
|
||||
"-m64": {0, pr.compileUnaryCallback},
|
||||
"-mstackrealign": {0, pr.compileUnaryCallback},
|
||||
"-maes": {0, pr.compileUnaryCallback},
|
||||
"-mno-aes": {0, pr.compileUnaryCallback},
|
||||
"-mavx": {0, pr.compileUnaryCallback},
|
||||
"-mno-avx": {0, pr.compileUnaryCallback},
|
||||
"-mavx2": {0, pr.compileUnaryCallback},
|
||||
"-mno-avx2": {0, pr.compileUnaryCallback},
|
||||
"-mno-red-zone": {0, pr.compileUnaryCallback},
|
||||
"-mmmx": {0, pr.compileUnaryCallback},
|
||||
"-mno-mmx": {0, pr.compileUnaryCallback},
|
||||
"-mno-global-merge": {0, pr.compileUnaryCallback}, //iam: linux kernel stuff
|
||||
"-mno-80387": {0, pr.compileUnaryCallback}, //iam: linux kernel stuff
|
||||
"-msse": {0, pr.compileUnaryCallback},
|
||||
"-mno-sse": {0, pr.compileUnaryCallback},
|
||||
"-msse2": {0, pr.compileUnaryCallback},
|
||||
"-mno-sse2": {0, pr.compileUnaryCallback},
|
||||
"-msse3": {0, pr.compileUnaryCallback},
|
||||
"-mno-sse3": {0, pr.compileUnaryCallback},
|
||||
"-mssse3": {0, pr.compileUnaryCallback},
|
||||
"-mno-ssse3": {0, pr.compileUnaryCallback},
|
||||
"-msse4": {0, pr.compileUnaryCallback},
|
||||
"-mno-sse4": {0, pr.compileUnaryCallback},
|
||||
"-msse4.1": {0, pr.compileUnaryCallback},
|
||||
"-mno-sse4.1": {0, pr.compileUnaryCallback},
|
||||
"-msse4.2": {0, pr.compileUnaryCallback},
|
||||
"-mno-sse4.2": {0, pr.compileUnaryCallback},
|
||||
"-msoft-float": {0, pr.compileUnaryCallback},
|
||||
"-m3dnow": {0, pr.compileUnaryCallback},
|
||||
"-mno-3dnow": {0, pr.compileUnaryCallback},
|
||||
"-m16": {0, pr.compileUnaryCallback}, //iam: linux kernel stuff
|
||||
"-m32": {0, pr.compileUnaryCallback},
|
||||
"-m64": {0, pr.compileUnaryCallback},
|
||||
"-mstackrealign": {0, pr.compileUnaryCallback},
|
||||
"-mretpoline-external-thunk": {0, pr.compileUnaryCallback}, //iam: linux kernel stuff
|
||||
"-mno-fp-ret-in-387": {0, pr.compileUnaryCallback}, //iam: linux kernel stuff
|
||||
"-mskip-rax-setup": {0, pr.compileUnaryCallback}, //iam: linux kernel stuff
|
||||
"-mindirect-branch-register": {0, pr.compileUnaryCallback}, //iam: linux kernel stuff
|
||||
|
||||
"-A": {1, pr.compileBinaryCallback},
|
||||
"-D": {1, pr.compileBinaryCallback},
|
||||
"-U": {1, pr.compileBinaryCallback},
|
||||
|
||||
"-P": {1, pr.compileUnaryCallback}, //iam: linux kernel stuff (linker script stuff)
|
||||
"-C": {1, pr.compileUnaryCallback}, //iam: linux kernel stuff (linker script stuff)
|
||||
|
||||
"-M": {0, pr.dependencyOnlyCallback},
|
||||
"-MM": {0, pr.dependencyOnlyCallback},
|
||||
"-MF": {1, pr.dependencyBinaryCallback},
|
||||
@ -198,6 +210,7 @@ func parse(argList []string) parserResult {
|
||||
"-Os": {0, pr.compileUnaryCallback},
|
||||
"-Ofast": {0, pr.compileUnaryCallback},
|
||||
"-Og": {0, pr.compileUnaryCallback},
|
||||
"-Oz": {0, pr.compileUnaryCallback}, //iam: linux kernel
|
||||
|
||||
"-Xclang": {1, pr.compileBinaryCallback},
|
||||
"-Xpreprocessor": {1, pr.defaultBinaryCallback},
|
||||
@ -254,6 +267,14 @@ func parse(argList []string) parserResult {
|
||||
`^--sysroot=.+$`: {0, pr.compileUnaryCallback},
|
||||
`^-print-prog-name=.*$`: {0, pr.compileUnaryCallback},
|
||||
`^-print-file-name=.*$`: {0, pr.compileUnaryCallback},
|
||||
`^-mstack-alignment=.+$`: {0, pr.compileUnaryCallback}, //iam: linux kernel stuff
|
||||
`^-march=.+$`: {0, pr.compileUnaryCallback}, //iam: linux kernel stuff
|
||||
`^-mregparm=.+$`: {0, pr.compileUnaryCallback}, //iam: linux kernel stuff
|
||||
`^-mcmodel=.+$`: {0, pr.compileUnaryCallback}, //iam: linux kernel stuff
|
||||
`^-mpreferred-stack-boundary=.+$`: {0, pr.compileUnaryCallback}, //iam: linux kernel stuff
|
||||
`^-mindirect-branch=.+$`: {0, pr.compileUnaryCallback}, //iam: linux kernel stuff
|
||||
`^--param=.+$`: {0, pr.compileUnaryCallback}, //iam: linux kernel stuff
|
||||
|
||||
}
|
||||
|
||||
for len(argList) > 0 {
|
||||
@ -266,17 +287,23 @@ func parse(argList []string) parserResult {
|
||||
// Else try to match a pattern
|
||||
} else {
|
||||
var listShift = 0
|
||||
var matched = false
|
||||
for pattern, fi := range argPatterns {
|
||||
var regExp = regexp.MustCompile(pattern)
|
||||
if regExp.MatchString(elem) {
|
||||
fi.handler(elem, argList[1:1+fi.arity])
|
||||
listShift = fi.arity
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
LogWarning("Did not recognize the compiler flag: %v\n", elem)
|
||||
//LogWarning("CC %v\n", pr.InputList)
|
||||
pr.compileUnaryCallback(elem, argList[1:1])
|
||||
}
|
||||
argList = argList[1+listShift:]
|
||||
}
|
||||
|
||||
}
|
||||
return pr
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ package shared
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@ -87,6 +88,10 @@ then LLVM_AR_NAME should be set to llvm-ar-3.5.
|
||||
|
||||
`
|
||||
|
||||
type sanityArgs struct {
|
||||
Environment bool
|
||||
}
|
||||
|
||||
// SanityCheck performs the environmental sanity check.
|
||||
//
|
||||
// Performs the following checks in order:
|
||||
@ -98,8 +103,14 @@ then LLVM_AR_NAME should be set to llvm-ar-3.5.
|
||||
//
|
||||
func SanityCheck() {
|
||||
|
||||
sa := parseSanitySwitches()
|
||||
|
||||
LogWrite("\nVersion info: gsanity-check version %v\nReleased: %v\n", gllvmVersion, gllvmReleaseDate)
|
||||
|
||||
if sa.Environment {
|
||||
printEnvironment()
|
||||
}
|
||||
|
||||
checkLogging()
|
||||
|
||||
checkOS()
|
||||
@ -116,6 +127,20 @@ func SanityCheck() {
|
||||
|
||||
}
|
||||
|
||||
func parseSanitySwitches() (sa sanityArgs) {
|
||||
sa = sanityArgs{
|
||||
Environment: false,
|
||||
}
|
||||
|
||||
environmentPtr := flag.Bool("e", false, "show environment")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
sa.Environment = *environmentPtr
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func checkOS() {
|
||||
|
||||
platform := runtime.GOOS
|
||||
@ -260,6 +285,6 @@ func checkLogging() {
|
||||
LogWrite("Logging level is set to UNKNOWN level %s, using default of ERROR.\n\n", LLVMLoggingLevel)
|
||||
}
|
||||
} else {
|
||||
LogWrite("Logging level not set, using default of ERROR.\n\n")
|
||||
LogWrite("Logging level not set, using default of WARNING.\n\n")
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
@ -57,3 +58,19 @@ func execCmd(cmdExecName string, args []string, workingDir string) (success bool
|
||||
success = (ecode == 0)
|
||||
return
|
||||
}
|
||||
|
||||
// Executes a command then returns the output as a string, err is either nil or the error.
|
||||
func runCmd(cmdExecName string, args []string) (output string, err error) {
|
||||
var outb bytes.Buffer
|
||||
var errb bytes.Buffer
|
||||
cmd := exec.Command(cmdExecName, args...)
|
||||
cmd.Stdout = &outb
|
||||
cmd.Stderr = &errb
|
||||
cmd.Stdin = os.Stdin
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
LogDebug("runCmd: error was %v\n", err)
|
||||
}
|
||||
output = outb.String()
|
||||
return
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user