package com.oracle.svm.hosted.image;
import static com.oracle.svm.core.SubstrateUtil.mangleName;
import static com.oracle.svm.core.util.VMError.shouldNotReachHere;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.lang.invoke.MethodType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.graalvm.collections.Pair;
import org.graalvm.compiler.asm.aarch64.AArch64Assembler;
import org.graalvm.compiler.code.CompilationResult;
import org.graalvm.compiler.core.common.CompressEncoding;
import org.graalvm.compiler.core.common.NumUtil;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.Indent;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.c.function.CFunctionPointer;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.objectfile.BasicProgbitsSectionImpl;
import com.oracle.objectfile.BuildDependency;
import com.oracle.objectfile.LayoutDecision;
import com.oracle.objectfile.LayoutDecisionMap;
import com.oracle.objectfile.ObjectFile;
import com.oracle.objectfile.ObjectFile.Element;
import com.oracle.objectfile.ObjectFile.ProgbitsSectionImpl;
import com.oracle.objectfile.ObjectFile.RelocationKind;
import com.oracle.objectfile.ObjectFile.Section;
import com.oracle.objectfile.SectionName;
import com.oracle.objectfile.debuginfo.DebugInfoProvider;
import com.oracle.svm.core.FrameAccess;
import com.oracle.svm.core.Isolates;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.c.CConst;
import com.oracle.svm.core.c.CGlobalDataImpl;
import com.oracle.svm.core.c.CHeader;
import com.oracle.svm.core.c.CHeader.Header;
import com.oracle.svm.core.c.CTypedef;
import com.oracle.svm.core.c.CUnsigned;
import com.oracle.svm.core.c.function.CEntryPointOptions.Publish;
import com.oracle.svm.core.c.function.GraalIsolateHeader;
import com.oracle.svm.core.config.ConfigurationValues;
import com.oracle.svm.core.graal.code.CGlobalDataInfo;
import com.oracle.svm.core.graal.code.CGlobalDataReference;
import com.oracle.svm.core.image.ImageHeapLayoutInfo;
import com.oracle.svm.core.image.ImageHeapPartition;
import com.oracle.svm.core.meta.SubstrateObjectConstant;
import com.oracle.svm.core.option.HostedOptionValues;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.hosted.NativeImageOptions;
import com.oracle.svm.hosted.c.CGlobalDataFeature;
import com.oracle.svm.hosted.c.NativeLibraries;
import com.oracle.svm.hosted.c.codegen.CSourceCodeWriter;
import com.oracle.svm.hosted.code.CEntryPointCallStubMethod;
import com.oracle.svm.hosted.code.CEntryPointCallStubSupport;
import com.oracle.svm.hosted.code.CEntryPointData;
import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo;
import com.oracle.svm.hosted.image.RelocatableBuffer.Info;
import com.oracle.svm.hosted.image.sources.SourceManager;
import com.oracle.svm.hosted.meta.HostedMetaAccess;
import com.oracle.svm.hosted.meta.HostedMethod;
import com.oracle.svm.hosted.meta.HostedUniverse;
import com.oracle.svm.hosted.meta.MethodPointer;
import com.oracle.svm.util.ReflectionUtil;
import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError;
import jdk.vm.ci.aarch64.AArch64;
import jdk.vm.ci.amd64.AMD64;
import jdk.vm.ci.code.Architecture;
import jdk.vm.ci.code.site.ConstantReference;
import jdk.vm.ci.code.site.DataSectionReference;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaMethod.Parameter;
import jdk.vm.ci.meta.ResolvedJavaType;
public abstract class NativeBootImage extends AbstractBootImage {
public static final long RWDATA_CGLOBALS_PARTITION_OFFSET = 0;
private final ObjectFile objectFile;
private final int wordSize;
private final Set<HostedMethod> uniqueEntryPoints = new HashSet<>();
private Section textSection;
private Section roDataSection;
private Section rwDataSection;
private Section heapSection;
public NativeBootImage(NativeImageKind k, HostedUniverse universe, HostedMetaAccess metaAccess, NativeLibraries nativeLibs, NativeImageHeap heap, NativeImageCodeCache codeCache,
List<HostedMethod> entryPoints, ClassLoader imageClassLoader) {
super(k, universe, metaAccess, nativeLibs, heap, codeCache, entryPoints, imageClassLoader);
uniqueEntryPoints.addAll(entryPoints);
int pageSize = NativeImageOptions.getPageSize();
objectFile = ObjectFile.getNativeObjectFile(pageSize);
objectFile.setByteOrder(ConfigurationValues.getTarget().arch.getByteOrder());
wordSize = FrameAccess.wordSize();
assert objectFile.getWordSizeInBytes() == wordSize;
}
@Override
public Section getTextSection() {
assert textSection != null;
return textSection;
}
@Override
public abstract String[] makeLaunchCommand(NativeImageKind k, String imageName, Path binPath, Path workPath, java.lang.reflect.Method method);
protected final void write(DebugContext context, Path outputFile) {
try {
Path outFileParent = outputFile.normalize().getParent();
if (outFileParent != null) {
Files.createDirectories(outFileParent);
}
try (FileChannel channel = FileChannel.open(outputFile, StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) {
objectFile.withDebugContext(context, "ObjectFile.write", () -> {
objectFile.write(channel);
});
}
} catch (Exception ex) {
throw shouldNotReachHere(ex);
}
resultingImageSize = (int) outputFile.toFile().length();
if (NativeImageOptions.PrintImageElementSizes.getValue()) {
for (Element e : objectFile.getElements()) {
System.out.printf("PrintImageElementSizes: size: %15d name: %s\n", e.getMemSize(objectFile.getDecisionsByElement()), e.getElementName());
}
}
}
void (Path outputDir, String imageName, boolean dynamic) {
Map<? extends Class<? extends Header>, List<HostedMethod>> hostedMethods = uniqueEntryPoints.stream()
.filter(this::shouldWriteHeader)
.map(m -> Pair.create(cHeader(m), m))
.collect(Collectors.groupingBy(Pair::getLeft, Collectors.mapping(Pair::getRight, Collectors.toList())));
hostedMethods.forEach((headerClass, methods) -> {
methods.sort(NativeBootImage::sortMethodsByFileNameAndPosition);
Header header = headerClass == Header.class ? defaultCHeaderAnnotation(imageName) : instantiateCHeader(headerClass);
writeHeaderFile(outputDir, header, methods, dynamic);
});
}
private void (Path outDir, Header header, List<HostedMethod> methods, boolean dynamic) {
CSourceCodeWriter writer = new CSourceCodeWriter(outDir.getParent());
String imageHeaderGuard = "__" + header.name().toUpperCase().replaceAll("[^A-Z0-9]", "_") + "_H";
String dynamicSuffix = dynamic ? "_dynamic.h" : ".h";
writer.appendln("#ifndef " + imageHeaderGuard);
writer.appendln("#define " + imageHeaderGuard);
writer.appendln();
writer.writeCStandardHeaders();
List<String> dependencies = header.dependsOn().stream()
.map(NativeBootImage::instantiateCHeader)
.map(depHeader -> "<" + depHeader.name() + dynamicSuffix + ">").collect(Collectors.toList());
writer.includeFiles(dependencies);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintWriter printWriter = new PrintWriter(baos);
header.writePreamble(printWriter);
printWriter.flush();
for (String line : baos.toString().split("\\r?\\n")) {
writer.appendln(line);
}
if (methods.size() > 0) {
writer.appendln();
writer.appendln("#if defined(__cplusplus)");
writer.appendln("extern \"C\" {");
writer.appendln("#endif");
writer.appendln();
methods.forEach(m -> writeMethodHeader(m, writer, dynamic));
writer.appendln("#if defined(__cplusplus)");
writer.appendln("}");
writer.appendln("#endif");
}
writer.appendln("#endif");
Path fileNamePath = outDir.getFileName();
if (fileNamePath == null) {
throw UserError.abort("Cannot determine header file name for directory %s", outDir);
} else {
String fileName = fileNamePath.resolve(header.name() + dynamicSuffix).toString();
writer.writeFile(fileName);
}
}
private static Class<? extends CHeader.Header> (HostedMethod entryPointStub) {
AnalysisMethod entryPoint = CEntryPointCallStubSupport.singleton().getMethodForStub((CEntryPointCallStubMethod) entryPointStub.wrapped.wrapped);
CHeader methodAnnotation = entryPoint.getDeclaredAnnotation(CHeader.class);
if (methodAnnotation != null) {
return methodAnnotation.value();
}
AnalysisType enclosingType = entryPoint.getDeclaringClass();
while (enclosingType != null) {
CHeader enclosing = enclosingType.getDeclaredAnnotation(CHeader.class);
if (enclosing != null) {
return enclosing.value();
}
enclosingType = enclosingType.getEnclosingType();
}
return CHeader.Header.class;
}
private static Header (Class<? extends CHeader.Header> header) {
try {
return ReflectionUtil.newInstance(header);
} catch (ReflectionUtilError ex) {
throw UserError.abort(ex.getCause(), "CHeader %s cannot be instantiated. Please make sure that it has a nullary constructor and is not abstract.", header.getName());
}
}
private static CHeader.Header (String defaultHeaderName) {
return new CHeader.Header() {
@Override
public String name() {
return defaultHeaderName;
}
@Override
public List<Class<? extends Header>> dependsOn() {
return Collections.singletonList(GraalIsolateHeader.class);
}
};
}
private static int sortMethodsByFileNameAndPosition(HostedMethod stub1, HostedMethod stub2) {
ResolvedJavaMethod rm1 = CEntryPointCallStubSupport.singleton().getMethodForStub((CEntryPointCallStubMethod) stub1.wrapped.wrapped).wrapped;
ResolvedJavaMethod rm2 = CEntryPointCallStubSupport.singleton().getMethodForStub((CEntryPointCallStubMethod) stub2.wrapped.wrapped).wrapped;
int fileComparison = rm1.getDeclaringClass().getSourceFileName().compareTo(rm2.getDeclaringClass().getSourceFileName());
if (fileComparison != 0) {
return fileComparison;
}
int rm1Line = rm1.getLineNumberTable() != null ? rm1.getLineNumberTable().getLineNumber(0) : -1;
int rm2Line = rm2.getLineNumberTable() != null ? rm2.getLineNumberTable().getLineNumber(0) : -1;
return rm1Line - rm2Line;
}
private void (HostedMethod m, CSourceCodeWriter writer, boolean dynamic) {
assert Modifier.isStatic(m.getModifiers()) : "Published methods that go into the header must be static.";
CEntryPointData cEntryPointData = (CEntryPointData) m.getWrapped().getEntryPointData();
String docComment = cEntryPointData.getDocumentation();
if (docComment != null && !docComment.isEmpty()) {
writer.appendln("/*");
Arrays.stream(docComment.split("\n")).forEach(l -> writer.appendln(" * " + l));
writer.appendln(" */");
}
if (dynamic) {
writer.append("typedef ");
}
AnnotatedType annotatedReturnType = getAnnotatedReturnType(m);
writer.append(CSourceCodeWriter.toCTypeName(m,
(ResolvedJavaType) m.getSignature().getReturnType(m.getDeclaringClass()),
Optional.ofNullable(annotatedReturnType.getAnnotation(CTypedef.class)).map(CTypedef::name),
false,
annotatedReturnType.isAnnotationPresent(CUnsigned.class),
metaAccess, nativeLibs));
writer.append(" ");
String symbolName = cEntryPointData.getSymbolName();
assert !symbolName.isEmpty();
if (dynamic) {
writer.append("(*").append(symbolName).append("_fn_t)");
} else {
writer.append(symbolName);
}
writer.append("(");
String sep = "";
AnnotatedType[] annotatedParameterTypes = getAnnotatedParameterTypes(m);
Parameter[] parameters = m.getParameters();
assert parameters != null;
for (int i = 0; i < m.getSignature().getParameterCount(false); i++) {
writer.append(sep);
sep = ", ";
writer.append(CSourceCodeWriter.toCTypeName(m,
(ResolvedJavaType) m.getSignature().getParameterType(i, m.getDeclaringClass()),
Optional.ofNullable(annotatedParameterTypes[i].getAnnotation(CTypedef.class)).map(CTypedef::name),
annotatedParameterTypes[i].isAnnotationPresent(CConst.class),
annotatedParameterTypes[i].isAnnotationPresent(CUnsigned.class),
metaAccess, nativeLibs));
if (parameters[i].isNamePresent()) {
writer.append(" ");
writer.append(parameters[i].getName());
}
}
writer.appendln(");");
writer.appendln();
}
private AnnotatedType getAnnotatedReturnType(HostedMethod hostedMethod) {
return getMethod(hostedMethod).getAnnotatedReturnType();
}
private AnnotatedType[] getAnnotatedParameterTypes(HostedMethod hostedMethod) {
return getMethod(hostedMethod).getAnnotatedParameterTypes();
}
private Method getMethod(HostedMethod hostedMethod) {
AnalysisMethod entryPoint = CEntryPointCallStubSupport.singleton().getMethodForStub(((CEntryPointCallStubMethod) hostedMethod.wrapped.wrapped));
Method method;
try {
method = entryPoint.getDeclaringClass().getJavaClass().getDeclaredMethod(entryPoint.getName(),
MethodType.fromMethodDescriptorString(entryPoint.getSignature().toMethodDescriptor(), imageClassLoader).parameterArray());
} catch (NoSuchMethodException e) {
throw shouldNotReachHere(e);
}
return method;
}
private boolean (HostedMethod method) {
Object data = method.getWrapped().getEntryPointData();
return data instanceof CEntryPointData && ((CEntryPointData) data).getPublishAs() == Publish.SymbolAndHeader;
}
private ObjectFile.Symbol defineDataSymbol(String name, Element section, long position) {
return objectFile.createDefinedSymbol(name, section, position, wordSize, false, true);
}
private ObjectFile.Symbol defineRelocationForSymbol(String name, long position) {
ObjectFile.Symbol symbol = null;
if (objectFile.getSymbolTable().getSymbol(name) == null) {
symbol = objectFile.createUndefinedSymbol(name, 0, true);
}
ProgbitsSectionImpl baseSectionImpl = (ProgbitsSectionImpl) rwDataSection.getImpl();
int offsetInSection = Math.toIntExact(RWDATA_CGLOBALS_PARTITION_OFFSET + position);
baseSectionImpl.markRelocationSite(offsetInSection, wordSize == 8 ? RelocationKind.DIRECT_8 : RelocationKind.DIRECT_4, name, false, 0L);
return symbol;
}
@Override
@SuppressWarnings("try")
public void build(DebugContext debug) {
try (DebugContext.Scope buildScope = debug.scope("NativeBootImage.build")) {
final CGlobalDataFeature cGlobals = CGlobalDataFeature.singleton();
long roSectionSize = codeCache.getAlignedConstantsSize();
long rwSectionSize = ConfigurationValues.getObjectLayout().alignUp(cGlobals.getSize());
ImageHeapLayoutInfo heapLayout = heap.getLayouter().layout(heap, objectFile.getPageSize());
assert !hasDuplicatedObjects(heap.getObjects()) : "heap.getObjects() must not contain any duplicates";
final int textSectionSize = codeCache.getCodeCacheSize();
final RelocatableBuffer textBuffer = new RelocatableBuffer(textSectionSize, objectFile.getByteOrder());
final NativeTextSectionImpl textImpl = NativeTextSectionImpl.factory(textBuffer, objectFile, codeCache);
textSection = objectFile.newProgbitsSection(SectionName.TEXT.getFormatDependentName(objectFile.getFormat()), objectFile.getPageSize(), false, true, textImpl);
boolean writable = SubstrateOptions.ForceNoROSectionRelocations.getValue();
final RelocatableBuffer roDataBuffer = new RelocatableBuffer(roSectionSize, objectFile.getByteOrder());
final ProgbitsSectionImpl roDataImpl = new BasicProgbitsSectionImpl(roDataBuffer.getBackingArray());
roDataSection = objectFile.newProgbitsSection(SectionName.RODATA.getFormatDependentName(objectFile.getFormat()), objectFile.getPageSize(), writable, false, roDataImpl);
final RelocatableBuffer rwDataBuffer = new RelocatableBuffer(rwSectionSize, objectFile.getByteOrder());
final ProgbitsSectionImpl rwDataImpl = new BasicProgbitsSectionImpl(rwDataBuffer.getBackingArray());
rwDataSection = objectFile.newProgbitsSection(SectionName.DATA.getFormatDependentName(objectFile.getFormat()), objectFile.getPageSize(), true, false, rwDataImpl);
objectFile.createDefinedSymbol(textSection.getName(), textSection, 0, 0, false, false);
objectFile.createDefinedSymbol("__svm_text_end", textSection, textSectionSize, 0, false, true);
objectFile.createDefinedSymbol(roDataSection.getName(), roDataSection, 0, 0, false, false);
objectFile.createDefinedSymbol(rwDataSection.getName(), rwDataSection, 0, 0, false, false);
NativeImageHeapWriter writer = new NativeImageHeapWriter(heap, heapLayout);
textImpl.writeTextSection(debug, textSection, entryPoints);
codeCache.writeConstants(writer, roDataBuffer);
cGlobals.writeData(rwDataBuffer,
(offset, symbolName) -> defineDataSymbol(symbolName, rwDataSection, offset + RWDATA_CGLOBALS_PARTITION_OFFSET),
(offset, symbolName) -> defineRelocationForSymbol(symbolName, offset));
defineDataSymbol(CGlobalDataInfo.CGLOBALDATA_BASE_SYMBOL_NAME, rwDataSection, RWDATA_CGLOBALS_PARTITION_OFFSET);
if (SubstrateOptions.GenerateDebugInfo.getValue(HostedOptionValues.singleton()) > 0) {
ImageSingletons.add(SourceManager.class, new SourceManager());
DebugInfoProvider provider = new NativeImageDebugInfoProvider(debug, codeCache, heap);
objectFile.installDebugInfo(provider);
}
int alignment = objectFile.getPageSize();
RelocatableBuffer heapSectionBuffer = new RelocatableBuffer(heapLayout.getImageHeapSize(), objectFile.getByteOrder());
ProgbitsSectionImpl heapSectionImpl = new BasicProgbitsSectionImpl(heapSectionBuffer.getBackingArray());
heapSection = objectFile.newProgbitsSection(SectionName.SVM_HEAP.getFormatDependentName(objectFile.getFormat()), alignment, writable, false, heapSectionImpl);
objectFile.createDefinedSymbol(heapSection.getName(), heapSection, 0, 0, false, false);
long offsetOfARelocatablePointer = writer.writeHeap(debug, heapSectionBuffer);
assert !SubstrateOptions.SpawnIsolates.getValue() || heapSectionBuffer.getByteBuffer().getLong((int) offsetOfARelocatablePointer) == 0L;
defineDataSymbol(Isolates.IMAGE_HEAP_BEGIN_SYMBOL_NAME, heapSection, 0);
defineDataSymbol(Isolates.IMAGE_HEAP_END_SYMBOL_NAME, heapSection, heapLayout.getImageHeapSize());
defineDataSymbol(Isolates.IMAGE_HEAP_RELOCATABLE_BEGIN_SYMBOL_NAME, heapSection, heapLayout.getReadOnlyRelocatableOffset());
defineDataSymbol(Isolates.IMAGE_HEAP_RELOCATABLE_END_SYMBOL_NAME, heapSection, heapLayout.getReadOnlyRelocatableOffset() + heapLayout.getReadOnlyRelocatableSize());
defineDataSymbol(Isolates.IMAGE_HEAP_A_RELOCATABLE_POINTER_SYMBOL_NAME, heapSection, offsetOfARelocatablePointer);
defineDataSymbol(Isolates.IMAGE_HEAP_WRITABLE_BEGIN_SYMBOL_NAME, heapSection, heapLayout.getWritableOffset());
defineDataSymbol(Isolates.IMAGE_HEAP_WRITABLE_END_SYMBOL_NAME, heapSection, heapLayout.getWritableOffset() + heapLayout.getWritableSize());
markRelocationSitesFromBuffer(textBuffer, textImpl);
markRelocationSitesFromBuffer(roDataBuffer, roDataImpl);
markRelocationSitesFromBuffer(rwDataBuffer, rwDataImpl);
markRelocationSitesFromBuffer(heapSectionBuffer, heapSectionImpl);
printHeapStatistics(heap.getLayouter().getPartitions());
}
}
private boolean hasDuplicatedObjects(Collection<ObjectInfo> objects) {
Set<ObjectInfo> deduplicated = Collections.newSetFromMap(new IdentityHashMap<>());
for (ObjectInfo info : objects) {
deduplicated.add(info);
}
return deduplicated.size() != heap.getObjectCount();
}
private void markRelocationSitesFromBuffer(RelocatableBuffer buffer, ProgbitsSectionImpl sectionImpl) {
for (Map.Entry<Integer, RelocatableBuffer.Info> entry : buffer.getSortedRelocations()) {
final int offset = entry.getKey();
final RelocatableBuffer.Info info = entry.getValue();
assert ConfigurationValues.getTarget().arch instanceof AArch64 || checkEmbeddedOffset(sectionImpl, offset, info);
if (info.getTargetObject() instanceof CFunctionPointer) {
markFunctionRelocationSite(sectionImpl, offset, info);
} else {
if (sectionImpl.getElement() == textSection) {
markDataRelocationSiteFromText(buffer, sectionImpl, offset, info);
} else {
final Object targetObject = info.getTargetObject();
final ObjectInfo targetObjectInfo = heap.getObjectInfo(targetObject);
markDataRelocationSite(sectionImpl, offset, info, targetObjectInfo);
}
}
}
}
private static boolean checkEmbeddedOffset(ProgbitsSectionImpl sectionImpl, final int offset, final RelocatableBuffer.Info info) {
final ByteBuffer dataBuf = ByteBuffer.wrap(sectionImpl.getContent()).order(sectionImpl.getElement().getOwner().getByteOrder());
if (info.getRelocationSize() == Long.BYTES) {
long value = dataBuf.getLong(offset);
assert value == 0 || value == 0xDEADDEADDEADDEADL : String.format("unexpected embedded offset: 0x%x, info: %s", value, info);
} else if (info.getRelocationSize() == Integer.BYTES) {
int value = dataBuf.getInt(offset);
assert value == 0 || value == 0xDEADDEAD : "unexpected embedded offset";
} else {
shouldNotReachHere("unsupported relocation size: " + info.getRelocationSize());
}
return true;
}
private static void markFunctionRelocationSite(final ProgbitsSectionImpl sectionImpl, final int offset, final RelocatableBuffer.Info info) {
assert info.getTargetObject() instanceof CFunctionPointer : "Wrong type for FunctionPointer relocation: " + info.getTargetObject().toString();
final int functionPointerRelocationSize = 8;
assert info.getRelocationSize() == functionPointerRelocationSize : "Function relocation: " + info.getRelocationSize() + " should be " + functionPointerRelocationSize + " bytes.";
ResolvedJavaMethod method = ((MethodPointer) info.getTargetObject()).getMethod();
sectionImpl.markRelocationSite(offset, RelocationKind.getDirect(functionPointerRelocationSize), localSymbolNameForMethod(method), false, 0L);
}
private static boolean isAddendAligned(Architecture arch, long addend, RelocationKind kind) {
if (arch instanceof AMD64) {
return true;
}
switch (kind) {
case AARCH64_R_AARCH64_LDST16_ABS_LO12_NC:
return (addend & 0x1) == 0;
case AARCH64_R_AARCH64_LDST32_ABS_LO12_NC:
return (addend & 0x3) == 0;
case AARCH64_R_AARCH64_LDST64_ABS_LO12_NC:
return (addend & 0x7) == 0;
}
return true;
}
private void markDataRelocationSite(ProgbitsSectionImpl sectionImpl, int offset, RelocatableBuffer.Info info, ObjectInfo targetObjectInfo) {
assert ConfigurationValues.getTarget().arch instanceof AArch64 || info.getRelocationSize() == 4 || info.getRelocationSize() == 8 : "AMD64 Data relocation size should be 4 or 8 bytes.";
assert targetObjectInfo != null;
String targetSectionName = heapSection.getName();
long address = targetObjectInfo.getAddress();
long relocationInfoAddend = info.hasExplicitAddend() ? info.getExplicitAddend() : 0L;
long relocationAddend = address + relocationInfoAddend;
sectionImpl.markRelocationSite(offset, info.getRelocationKind(), targetSectionName, false, relocationAddend);
}
private void markDataRelocationSiteFromText(RelocatableBuffer buffer, final ProgbitsSectionImpl sectionImpl, final int offset, final Info info) {
Architecture arch = ConfigurationValues.getTarget().arch;
assert arch instanceof AArch64 || ((info.getRelocationSize() == 4) || (info.getRelocationSize() == 8)) : "AMD64 Data relocation size should be 4 or 8 bytes. Got size: " +
info.getRelocationSize();
Object target = info.getTargetObject();
if (target instanceof DataSectionReference) {
long addend = ((DataSectionReference) target).getOffset() - info.getExplicitAddend();
assert isAddendAligned(arch, addend, info.getRelocationKind()) : "improper addend alignment";
sectionImpl.markRelocationSite(offset, info.getRelocationKind(), roDataSection.getName(), false, addend);
} else if (target instanceof CGlobalDataReference) {
CGlobalDataReference ref = (CGlobalDataReference) target;
CGlobalDataInfo dataInfo = ref.getDataInfo();
CGlobalDataImpl<?> data = dataInfo.getData();
long addend = RWDATA_CGLOBALS_PARTITION_OFFSET + dataInfo.getOffset() - info.getExplicitAddend();
assert isAddendAligned(arch, addend, info.getRelocationKind()) : "improper addend alignment";
sectionImpl.markRelocationSite(offset, info.getRelocationKind(), rwDataSection.getName(), false, addend);
if (dataInfo.isSymbolReference()) {
if (objectFile.getSymbolTable().getSymbol(data.symbolName) == null) {
objectFile.createUndefinedSymbol(data.symbolName, 0, true);
}
ProgbitsSectionImpl baseSectionImpl = (ProgbitsSectionImpl) rwDataSection.getImpl();
int offsetInSection = Math.toIntExact(RWDATA_CGLOBALS_PARTITION_OFFSET + dataInfo.getOffset());
baseSectionImpl.markRelocationSite(offsetInSection, RelocationKind.getDirect(wordSize), data.symbolName, false, 0L);
}
} else if (target instanceof ConstantReference) {
Object object = SubstrateObjectConstant.asObject(((ConstantReference) target).getConstant());
long address = heap.getObjectInfo(object).getAddress();
int encShift = ImageSingletons.lookup(CompressEncoding.class).getShift();
long targetValue = address >>> encShift;
assert (targetValue << encShift) == address : "Reference compression shift discards non-zero bits: " + Long.toHexString(address);
ByteBuffer bufferBytes = buffer.getByteBuffer();
if (arch instanceof AMD64) {
assert (info.getRelocationKind() == RelocationKind.DIRECT_4) || (info.getRelocationKind() == RelocationKind.DIRECT_8);
if (info.getRelocationSize() == Long.BYTES) {
bufferBytes.putLong(offset, targetValue);
} else if (info.getRelocationSize() == Integer.BYTES) {
bufferBytes.putInt(offset, NumUtil.safeToInt(targetValue));
} else {
new Exception().printStackTrace();
shouldNotReachHere("Unsupported object reference size: " + info.getRelocationSize());
}
} else if (arch instanceof AArch64) {
int patchValue = 0;
switch (info.getRelocationKind()) {
case AARCH64_R_MOVW_UABS_G0:
case AARCH64_R_MOVW_UABS_G0_NC:
patchValue = (int) targetValue & 0xFFFF;
break;
case AARCH64_R_MOVW_UABS_G1:
case AARCH64_R_MOVW_UABS_G1_NC:
patchValue = (int) (targetValue >> 16) & 0xFFFF;
break;
case AARCH64_R_MOVW_UABS_G2:
case AARCH64_R_MOVW_UABS_G2_NC:
patchValue = (int) (targetValue >> 32) & 0xFFFF;
break;
case AARCH64_R_MOVW_UABS_G3:
patchValue = (int) (targetValue >> 48) & 0xFFFF;
break;
default:
throw shouldNotReachHere("Unsupported AArch64 relocation kind: " + info.getRelocationKind());
}
switch (info.getRelocationKind()) {
case AARCH64_R_MOVW_UABS_G0:
case AARCH64_R_MOVW_UABS_G1:
case AARCH64_R_MOVW_UABS_G2:
assert (patchValue & 0xFFFF) == patchValue : "value to patch does not fit";
}
int originalInst = bufferBytes.getInt(offset);
int newInst = AArch64Assembler.PatcherUtil.patchMov(originalInst, patchValue);
bufferBytes.putInt(offset, newInst);
}
} else {
throw shouldNotReachHere("Unsupported target object for relocation in text section");
}
}
public static String localSymbolNameForMethod(java.lang.reflect.Method m) {
return SubstrateUtil.uniqueShortName(m);
}
public static String localSymbolNameForMethod(ResolvedJavaMethod sm) {
return SubstrateOptions.ImageSymbolsPrefix.getValue() + SubstrateUtil.uniqueShortName(sm);
}
public static String globalSymbolNameForMethod(java.lang.reflect.Method m) {
return mangleName(SubstrateUtil.uniqueShortName(m));
}
public static String globalSymbolNameForMethod(ResolvedJavaMethod sm) {
return mangleName(SubstrateUtil.uniqueShortName(sm));
}
@Override
public ObjectFile getOrCreateDebugObjectFile() {
assert objectFile != null;
return objectFile;
}
private void printHeapStatistics(ImageHeapPartition[] partitions) {
if (NativeImageOptions.PrintHeapHistogram.getValue()) {
ObjectGroupHistogram.print(heap);
printHistogram(partitions);
}
if (NativeImageOptions.PrintImageHeapPartitionSizes.getValue()) {
printSizes(partitions);
}
}
private void printHistogram(ImageHeapPartition[] partitions) {
for (ImageHeapPartition partition : partitions) {
printHistogram(partition, heap.getObjects());
}
}
private static void printSizes(ImageHeapPartition[] partitions) {
for (ImageHeapPartition partition : partitions) {
printSize(partition);
}
}
private static void printHistogram(ImageHeapPartition partition, Iterable<ObjectInfo> objects) {
HeapHistogram histogram = new HeapHistogram();
Set<ObjectInfo> uniqueObjectInfo = new HashSet<>();
long uniqueCount = 0L;
long uniqueSize = 0L;
long canonicalizedCount = 0L;
long canonicalizedSize = 0L;
for (ObjectInfo info : objects) {
if (partition == info.getPartition()) {
if (uniqueObjectInfo.add(info)) {
histogram.add(info, info.getSize());
uniqueCount += 1L;
uniqueSize += info.getSize();
} else {
canonicalizedCount += 1L;
canonicalizedSize += info.getSize();
}
}
}
long nonuniqueCount = uniqueCount + canonicalizedCount;
long nonuniqueSize = uniqueSize + canonicalizedSize;
assert partition.getSize() >= nonuniqueSize : "the total size can contain some overhead";
double countPercent = 100.0D * ((double) uniqueCount / (double) nonuniqueCount);
double sizePercent = 100.0D * ((double) uniqueSize / (double) nonuniqueSize);
double sizeOverheadPercent = 100.0D * (1.0D - ((double) partition.getSize() / (double) nonuniqueSize));
histogram.printHeadings(String.format("=== Partition: %s count: %d / %d = %.1f%% object size: %d / %d = %.1f%% total size: %d (%.1f%% overhead) ===",
partition.getName(),
uniqueCount, nonuniqueCount, countPercent,
uniqueSize, nonuniqueSize, sizePercent,
partition.getSize(), sizeOverheadPercent));
histogram.print();
}
private static void printSize(ImageHeapPartition partition) {
System.out.printf("PrintImageHeapPartitionSizes: partition: %s size: %d%n", partition.getName(), partition.getSize());
}
public abstract static class NativeTextSectionImpl extends BasicProgbitsSectionImpl {
public static NativeTextSectionImpl factory(RelocatableBuffer relocatableBuffer, ObjectFile objectFile, NativeImageCodeCache codeCache) {
return codeCache.getTextSectionImpl(relocatableBuffer, objectFile, codeCache);
}
private Element getRodataSection() {
return getElement().getOwner().elementForName(SectionName.RODATA.getFormatDependentName(getElement().getOwner().getFormat()));
}
@Override
public Set<BuildDependency> getDependencies(Map<Element, LayoutDecisionMap> decisions) {
HashSet<BuildDependency> deps = ObjectFile.minimalDependencies(decisions, getElement());
LayoutDecision ourContent = decisions.get(getElement()).getDecision(LayoutDecision.Kind.CONTENT);
LayoutDecision ourVaddr = decisions.get(getElement()).getDecision(LayoutDecision.Kind.VADDR);
LayoutDecision rodataVaddr = decisions.get(getRodataSection()).getDecision(LayoutDecision.Kind.VADDR);
deps.add(BuildDependency.createOrGet(ourContent, ourVaddr));
deps.add(BuildDependency.createOrGet(ourContent, rodataVaddr));
return deps;
}
@Override
public byte[] getOrDecideContent(Map<Element, LayoutDecisionMap> alreadyDecided, byte[] contentHint) {
return getContent();
}
protected abstract void defineMethodSymbol(String name, boolean global, Element section, HostedMethod method, CompilationResult result);
@SuppressWarnings("try")
protected void writeTextSection(DebugContext debug, final Section textSection, final List<HostedMethod> entryPoints) {
try (Indent indent = debug.logAndIndent("TextImpl.writeTextSection")) {
final Map<String, HostedMethod> methodsBySignature = new HashMap<>();
for (Map.Entry<HostedMethod, CompilationResult> ent : codeCache.getCompilations().entrySet()) {
final String symName = localSymbolNameForMethod(ent.getKey());
final String signatureString = SubstrateUtil.uniqueShortName(ent.getKey());
final HostedMethod existing = methodsBySignature.get(signatureString);
HostedMethod current = ent.getKey();
if (existing != null) {
final ResolvedJavaType existingReturnType = existing.getSignature().getReturnType(null).resolve(existing.getDeclaringClass());
final ResolvedJavaType currentReturnType = current.getSignature().getReturnType(null).resolve(current.getDeclaringClass());
if (existingReturnType.isAssignableFrom(currentReturnType)) {
final HostedMethod replaced = methodsBySignature.put(signatureString, current);
assert replaced.equals(existing);
}
} else {
methodsBySignature.put(signatureString, current);
}
defineMethodSymbol(symName, false, textSection, current, ent.getValue());
}
for (Map.Entry<String, HostedMethod> ent : methodsBySignature.entrySet()) {
HostedMethod method = ent.getValue();
Object data = method.getWrapped().getEntryPointData();
CEntryPointData cEntryData = (data instanceof CEntryPointData) ? (CEntryPointData) data : null;
if (cEntryData != null && cEntryData.getPublishAs() == Publish.NotPublished) {
continue;
}
final int entryPointIndex = entryPoints.indexOf(method);
if (entryPointIndex != -1) {
final String mangledSignature = mangleName(ent.getKey());
assert mangledSignature.equals(globalSymbolNameForMethod(method));
defineMethodSymbol(mangledSignature, true, textSection, method, null);
if (cEntryData != null) {
assert !cEntryData.getSymbolName().isEmpty();
defineMethodSymbol(cEntryData.getSymbolName(), true, textSection, method, codeCache.getCompilations().get(method));
}
}
}
assert !textBuffer.hasRelocations();
codeCache.patchMethods(debug, textBuffer, objectFile);
codeCache.writeCode(textBuffer);
}
}
protected NativeTextSectionImpl(RelocatableBuffer relocatableBuffer, ObjectFile objectFile, NativeImageCodeCache codeCache) {
super(relocatableBuffer.getBackingArray());
this.textBuffer = relocatableBuffer;
this.objectFile = objectFile;
this.codeCache = codeCache;
}
protected final RelocatableBuffer textBuffer;
protected final ObjectFile objectFile;
protected final NativeImageCodeCache codeCache;
}
}