Skip to content

Commit

Permalink
sulong: Support for rpath library locating
Browse files Browse the repository at this point in the history
  • Loading branch information
zapster committed Jul 10, 2019
1 parent 093d59f commit 443594b
Show file tree
Hide file tree
Showing 42 changed files with 1,007 additions and 62 deletions.
1 change: 1 addition & 0 deletions sulong/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ New features:

Improvements:

* Support locating dynamic libraries relatively using (`rpath`).
* Improved display of pointers to foreign objects in the LLVM debugger.
When inspecting pointer values that point somewhere inside a foreign object,
the debugger now allows inspecting the original foreign object, not just the
Expand Down
1 change: 1 addition & 0 deletions sulong/mx.sulong/mx_sulong.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
# re-export SulongTestSuite class so it can be used from suite.py
from mx_testsuites import SulongTestSuite #pylint: disable=unused-import
from mx_testsuites import ExternalTestSuite #pylint: disable=unused-import
from mx_testsuites import GlobNativeProject #pylint: disable=unused-import

_suite = mx.suite('sulong')
_mx = join(_suite.dir, "mx.sulong")
Expand Down
27 changes: 27 additions & 0 deletions sulong/mx.sulong/mx_testsuites.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from __future__ import print_function

import fnmatch
import glob

import mx
import mx_unittest
Expand Down Expand Up @@ -263,3 +264,29 @@ def getBuildEnv(self, replaceVar=mx_subst.path_substitutions):
env['VPATH'] = ':'.join(roots)
env['TESTFILE'] = self.getTestFile()
return env


class GlobNativeProject(mx.NativeProject):
def __init__(self, suite, name, deps, workingSets, subDir, results=None, output=None, **args):
projectDir = args.pop('dir', None)
if projectDir:
d = os.path.join(suite.dir, projectDir)
elif subDir is None:
d = os.path.join(suite.dir, name)
else:
d = os.path.join(suite.dir, subDir, name)
super(GlobNativeProject, self).__init__(suite, name, subDir, [], deps, workingSets, results, output, d, **args)

def getResults(self, replaceVar=mx_subst.results_substitutions):
results = super(GlobNativeProject, self).getResults(replaceVar)

def _glob(pathname):
return glob.glob(pathname) or mx.abort(
'Glob pattern "{}" did not return any file'.format(os.path.relpath(pathname, self.getOutput(replaceVar))))
results = [expanded for entry in results for expanded in _glob(entry)]
return results

def getBuildEnv(self, replaceVar=mx_subst.path_substitutions):
env = super(GlobNativeProject, self).getBuildEnv(replaceVar=replaceVar)
env['SRC_DIR'] = self.dir
return env
26 changes: 26 additions & 0 deletions sulong/mx.sulong/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,31 @@
"testProject": True,
"defaultBuild": False,
},
"com.oracle.truffle.llvm.tests.linker" : {
"class" : "GlobNativeProject",
"subDir" : "tests",
"native": True,
"vpath": True,
"buildEnv" : {
"OS" : "<os>",
"CLANG": "<toolchainGetToolPath:native,CC>",
},
"dependencies" : [
"SULONG_TEST",
"SULONG_TOOLCHAIN_LAUNCHERS",
"SULONG_BOOTSTRAP_TOOLCHAIN",
],
"results" : [
"linker/ref.out",
"linker/lib/<libsuffix:*>",
"rpath/lib/<libsuffix:*>",
"rpath/<libsuffix:*>",
"rpath/not-inherited/ref.out",
"rpath/main-override/ref.out",
],
"testProject" : True,
"defaultBuild" : False,
},
"gcc_c" : {
"subDir" : "tests/gcc",
"class" : "ExternalTestSuite",
Expand Down Expand Up @@ -994,6 +1019,7 @@
"com.oracle.truffle.llvm.tests.sulongavx",
"com.oracle.truffle.llvm.tests.sulongcpp",
"com.oracle.truffle.llvm.tests.libc",
"com.oracle.truffle.llvm.tests.linker",
],
"license" : "BSD-new",
"testDistribution" : True,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ protected Object doIntrinsic(Object value,
@CachedContext(LLVMLanguage.class) LLVMContext context,
@Cached("createReadString()") LLVMReadStringNode readId) {
String name = readId.executeWithTarget(value);
context.addExternalLibrary(name, true);
context.addExternalLibrary(name, true, "<truffle_load_library>");
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,22 @@
*/
package com.oracle.truffle.llvm.parser.binary;

import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.llvm.parser.elf.ElfDynamicSection;
import com.oracle.truffle.llvm.parser.elf.ElfFile;
import com.oracle.truffle.llvm.parser.elf.ElfLibraryLocator;
import com.oracle.truffle.llvm.parser.elf.ElfSectionHeaderTable;
import com.oracle.truffle.llvm.parser.macho.MachOFile;
import com.oracle.truffle.llvm.parser.macho.MachOLibraryLocator;
import com.oracle.truffle.llvm.parser.macho.Xar;
import com.oracle.truffle.llvm.parser.scanner.BitStream;
import com.oracle.truffle.llvm.runtime.DefaultLibraryLocator;
import com.oracle.truffle.llvm.runtime.LLVMContext;
import com.oracle.truffle.llvm.runtime.LibraryLocator;
import org.graalvm.polyglot.io.ByteSequence;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -87,20 +95,39 @@ public static Magic get(BitStream b) {
}
}

public static BinaryParserResult parse(ByteSequence bytes) {
private List<String> libraries = new ArrayList<>();
private List<String> paths = new ArrayList<>();
private LibraryLocator locator = DefaultLibraryLocator.INSTANCE;

public static BinaryParserResult parse(ByteSequence bytes, Source bcSource, LLVMContext context) {
return new BinaryParser().parseInternal(bytes, bcSource, context);
}

private BinaryParserResult parseInternal(ByteSequence bytes, Source bcSource, LLVMContext context) {
assert bytes != null;

List<String> libraries = new ArrayList<>();
List<String> paths = new ArrayList<>();
ByteSequence bitcode = parseBitcode(bytes, libraries, paths);
ByteSequence bitcode = parseBitcode(bytes, bcSource);
if (bitcode == null) {
// unsupported file
return null;
}
return new BinaryParserResult(libraries, paths, bitcode);
context.traceLoader("parse bitcode=%s\n", bcSource.getPath());
return new BinaryParserResult(libraries, paths, bitcode, locator);
}

public static String getOrigin(Source source) {
String sourcePath = source.getPath();
if (sourcePath == null) {
return null;
}
Path parent = Paths.get(sourcePath).getParent();
if (parent == null) {
return null;
}
return parent.toString();
}

private static ByteSequence parseBitcode(ByteSequence bytes, List<String> libraries, List<String> paths) {
private ByteSequence parseBitcode(ByteSequence bytes, Source source) {
BitStream b = BitStream.create(bytes);
Magic magicWord = Magic.get(b);
switch (magicWord) {
Expand All @@ -123,8 +150,9 @@ private static ByteSequence parseBitcode(ByteSequence bytes, List<String> librar
}
ElfDynamicSection dynamicSection = elfFile.getDynamicSection();
if (dynamicSection != null) {
libraries.addAll(dynamicSection.getDTNeeded());
paths.addAll(dynamicSection.getDTRPath());
List<String> elfLibraries = dynamicSection.getDTNeeded();
libraries.addAll(elfLibraries);
locator = new ElfLibraryLocator(elfFile, source);
}
long elfOffset = llvmbc.getOffset();
long elfSize = llvmbc.getSize();
Expand All @@ -135,20 +163,23 @@ private static ByteSequence parseBitcode(ByteSequence bytes, List<String> librar
case MH_CIGAM_64:
MachOFile machOFile = MachOFile.create(bytes);

libraries.addAll(machOFile.getDyLibs());
String origin = getOrigin(source);
List<String> machoLibraries = machOFile.getDyLibs(origin);
locator = new MachOLibraryLocator(machOFile, source);
libraries.addAll(machoLibraries);

ByteSequence machoBitcode = machOFile.extractBitcode();
if (machoBitcode == null) {
return null;
}
return parseBitcode(machoBitcode, libraries, paths);
return parseBitcode(machoBitcode, source);
case XAR_MAGIC:
Xar xarFile = Xar.create(bytes);
ByteSequence xarBitcode = xarFile.extractBitcode();
if (xarBitcode == null) {
return null;
}
return parseBitcode(xarBitcode, libraries, paths);
return parseBitcode(xarBitcode, source);
default:
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.Collections;
import java.util.List;

import com.oracle.truffle.llvm.runtime.LibraryLocator;
import org.graalvm.polyglot.io.ByteSequence;

/**
Expand All @@ -43,11 +44,13 @@ public final class BinaryParserResult {
private final List<String> libraries;
private final List<String> paths;
private final ByteSequence bitcode;
private final LibraryLocator locator;

BinaryParserResult(List<String> libraries, List<String> paths, ByteSequence bitcode) {
BinaryParserResult(List<String> libraries, List<String> paths, ByteSequence bitcode, LibraryLocator locator) {
this.libraries = libraries;
this.paths = paths;
this.bitcode = bitcode;
this.locator = locator;
}

public List<String> getLibraries() {
Expand All @@ -62,4 +65,7 @@ public ByteSequence getBitcode() {
return bitcode;
}

public LibraryLocator getLocator() {
return locator;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.graalvm.polyglot.io.ByteSequence;

public final class ElfDynamicSection {
Expand Down Expand Up @@ -114,8 +117,8 @@ public List<String> getDTNeeded() {
return getEntry(DT_NEEDED);
}

public List<String> getDTRunPath() {
return getEntry(DT_RUNPATH);
public Stream<String> getDTRunPathStream() {
return getEntryStream(DT_RUNPATH);
}

public List<String> getDTRPath() {
Expand All @@ -132,7 +135,11 @@ private static ElfSectionHeaderTable.Entry getDynamiSHEntry(ElfSectionHeaderTabl
}

private List<String> getEntry(int tag) {
return Arrays.stream(entries).filter(e -> e.getTag() == tag).map(e -> getString(e.getValue())).collect(Collectors.toList());
return getEntryStream(tag).collect(Collectors.toList());
}

private Stream<String> getEntryStream(int tag) {
return Arrays.stream(entries).filter(e -> e.getTag() == tag).map(e -> getString(e.getValue()));
}

private String getString(long offset) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.oracle.truffle.llvm.parser.elf;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.llvm.parser.binary.BinaryParser;
import com.oracle.truffle.llvm.runtime.DefaultLibraryLocator;
import com.oracle.truffle.llvm.runtime.LLVMContext;
import com.oracle.truffle.llvm.runtime.LibraryLocator;

/**
* Locates libraries from {@link ElfFile ELF files}.
*/
public final class ElfLibraryLocator extends LibraryLocator {
/** Extra library paths local to the module. */
private final List<String> localPaths;

private static final Pattern RPATH_PATTERN = Pattern.compile("\\$(ORIGIN|\\{ORIGIN\\})");

private static class Replacer {
private final Source source;
private String origin;

Replacer(Source source) {
this.source = source;
}

/**
* Replaces special rpath tokens. Currently, only {@code $ORIGIN} is supported and will be
* replace by the directory containing executable or shared object.
*/
public String replace(String e) {
if (origin == null) {
origin = BinaryParser.getOrigin(source);
}
if (origin == null) {
return e;
}
return RPATH_PATTERN.matcher(e).replaceAll(origin);
}
}

public ElfLibraryLocator(ElfFile elfFile, Source source) {
List<String> elfPaths = null;
ElfDynamicSection dynamicSection = elfFile.getDynamicSection();
if (dynamicSection != null) {
elfPaths = dynamicSection.getDTRunPathStream().map(new Replacer(source)::replace).collect(Collectors.toList());
if (elfPaths.isEmpty()) {
elfPaths = dynamicSection.getDTRPath();
}
}
this.localPaths = elfPaths;
}

@Override
public Path locateLibrary(LLVMContext context, String lib, Object reason) {
Path libPath = Paths.get(lib);
if (libPath.isAbsolute()) {
return DefaultLibraryLocator.locateAbsolute(context, lib, libPath);
}
Path path = DefaultLibraryLocator.locateGlobal(context, lib);
if (path != null) {
return path;
}

if (localPaths != null) {
// search file local paths
context.traceLoaderSearchPath(localPaths, reason);
for (String p : localPaths) {
Path absPath = Paths.get(p, lib);
context.traceLoaderTry(absPath);
if (absPath.toFile().exists()) {
return absPath;
}
}
}

context.traceLoaderTry(libPath);
return libPath;
}
}
Loading

0 comments on commit 443594b

Please sign in to comment.