Java(tm) 二进制内核 Linux 支持 v1.03

Linux 击败了所有其他系统!当所有其他操作系统都在谈论在操作系统中直接支持 Java 二进制文件时,Linux 正在这样做!

在完成以下操作后,您可以像执行任何其他程序一样执行 Java 应用程序和 Java Applet

  1. 您必须首先安装 Linux 的 Java 开发工具包。Linux 上的 Java HOWTO 提供了获取和安装的详细信息。可以在以下位置找到此 HOWTO

    您还应该设置合理的 CLASSPATH 环境变量,以使用任何使用非标准类(不包含在与应用程序本身相同的目录中)的 Java 应用程序。

  2. 您必须将 BINFMT_MISC 编译为模块或编译到内核中 (CONFIG_BINFMT_MISC) 并正确设置它。如果您选择将其编译为模块,则必须使用 modprobe/insmod 手动插入它,因为 binfmt_misc 无法轻松支持 kmod。请阅读此目录中的“binfmt_misc.txt”文件,以了解有关配置过程的更多信息。

  3. 将以下配置项添加到 binfmt_misc(您现在应该已经阅读了 binfmt_misc.txt):支持 Java 应用程序

    ':Java:M::\xca\xfe\xba\xbe::/usr/local/bin/javawrapper:'
    

    支持可执行 Jar 文件

    ':ExecutableJAR:E::jar::/usr/local/bin/jarwrapper:'
    

    支持 Java Applet

    ':Applet:E::html::/usr/bin/appletviewer:'
    

    或者以下内容,如果您想更具选择性

    ':Applet:M::<!--applet::/usr/bin/appletviewer:'
    

    当然,您必须修复路径名。本文档中给出的路径/文件名与 Debian 2.1 系统匹配。(即,jdk 安装在 /usr 中,本文档中的自定义包装器在 /usr/local 中)

    请注意,对于更具选择性的 applet 支持,您必须修改现有的 html 文件,使其在第一行包含 <!--applet-->< 必须是第一个字符!),以便使此方法生效!

    对于已编译的 Java 程序,您需要一个类似以下的包装脚本(这是因为 Java 在处理文件名时存在问题),再次修复脚本和上面给定的配置字符串中的路径名。

    您还需要脚本后面的小程序。像这样编译

    gcc -O2 -o javaclassname javaclassname.c
    

    并将其粘贴到 /usr/local/bin

    javawrapper shellscript 和 javaclassname 程序均由 Colin J. Watson <cjw44@cam.ac.uk> 提供。

Javawrapper shell 脚本

#!/bin/bash
# /usr/local/bin/javawrapper - the wrapper for binfmt_misc/java

if [ -z "$1" ]; then
      exec 1>&2
      echo Usage: $0 class-file
      exit 1
fi

CLASS=$1
FQCLASS=`/usr/local/bin/javaclassname $1`
FQCLASSN=`echo $FQCLASS | sed -e 's/^.*\.\([^.]*\)$/\1/'`
FQCLASSP=`echo $FQCLASS | sed -e 's-\.-/-g' -e 's-^[^/]*$--' -e 's-/[^/]*$--'`

# for example:
# CLASS=Test.class
# FQCLASS=foo.bar.Test
# FQCLASSN=Test
# FQCLASSP=foo/bar

unset CLASSBASE

declare -i LINKLEVEL=0

while :; do
      if [ "`basename $CLASS .class`" == "$FQCLASSN" ]; then
              # See if this directory works straight off
              cd -L `dirname $CLASS`
              CLASSDIR=$PWD
              cd $OLDPWD
              if echo $CLASSDIR | grep -q "$FQCLASSP$"; then
                      CLASSBASE=`echo $CLASSDIR | sed -e "s.$FQCLASSP$.."`
                      break;
              fi
              # Try dereferencing the directory name
              cd -P `dirname $CLASS`
              CLASSDIR=$PWD
              cd $OLDPWD
              if echo $CLASSDIR | grep -q "$FQCLASSP$"; then
                      CLASSBASE=`echo $CLASSDIR | sed -e "s.$FQCLASSP$.."`
                      break;
              fi
              # If no other possible filename exists
              if [ ! -L $CLASS ]; then
                      exec 1>&2
                      echo $0:
                      echo "  $CLASS should be in a" \
                           "directory tree called $FQCLASSP"
                      exit 1
              fi
      fi
      if [ ! -L $CLASS ]; then break; fi
      # Go down one more level of symbolic links
      let LINKLEVEL+=1
      if [ $LINKLEVEL -gt 5 ]; then
              exec 1>&2
              echo $0:
              echo "  Too many symbolic links encountered"
              exit 1
      fi
      CLASS=`ls --color=no -l $CLASS | sed -e 's/^.* \([^ ]*\)$/\1/'`
done

if [ -z "$CLASSBASE" ]; then
      if [ -z "$FQCLASSP" ]; then
              GOODNAME=$FQCLASSN.class
      else
              GOODNAME=$FQCLASSP/$FQCLASSN.class
      fi
      exec 1>&2
      echo $0:
      echo "  $FQCLASS should be in a file called $GOODNAME"
      exit 1
fi

if ! echo $CLASSPATH | grep -q "^\(.*:\)*$CLASSBASE\(:.*\)*"; then
      # class is not in CLASSPATH, so prepend dir of class to CLASSPATH
      if [ -z "${CLASSPATH}" ] ; then
              export CLASSPATH=$CLASSBASE
      else
              export CLASSPATH=$CLASSBASE:$CLASSPATH
      fi
fi

shift
/usr/bin/java $FQCLASS "$@"

javaclassname.c

/* javaclassname.c
 *
 * Extracts the class name from a Java class file; intended for use in a Java
 * wrapper of the type supported by the binfmt_misc option in the Linux kernel.
 *
 * Copyright (C) 1999 Colin J. Watson <[email protected]>.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>

/* From Sun's Java VM Specification, as tag entries in the constant pool. */

#define CP_UTF8 1
#define CP_INTEGER 3
#define CP_FLOAT 4
#define CP_LONG 5
#define CP_DOUBLE 6
#define CP_CLASS 7
#define CP_STRING 8
#define CP_FIELDREF 9
#define CP_METHODREF 10
#define CP_INTERFACEMETHODREF 11
#define CP_NAMEANDTYPE 12
#define CP_METHODHANDLE 15
#define CP_METHODTYPE 16
#define CP_INVOKEDYNAMIC 18

/* Define some commonly used error messages */

#define seek_error() error("%s: Cannot seek\n", program)
#define corrupt_error() error("%s: Class file corrupt\n", program)
#define eof_error() error("%s: Unexpected end of file\n", program)
#define utf8_error() error("%s: Only ASCII 1-255 supported\n", program);

char *program;

long *pool;

u_int8_t read_8(FILE *classfile);
u_int16_t read_16(FILE *classfile);
void skip_constant(FILE *classfile, u_int16_t *cur);
void error(const char *format, ...);
int main(int argc, char **argv);

/* Reads in an unsigned 8-bit integer. */
u_int8_t read_8(FILE *classfile)
{
      int b = fgetc(classfile);
      if(b == EOF)
              eof_error();
      return (u_int8_t)b;
}

/* Reads in an unsigned 16-bit integer. */
u_int16_t read_16(FILE *classfile)
{
      int b1, b2;
      b1 = fgetc(classfile);
      if(b1 == EOF)
              eof_error();
      b2 = fgetc(classfile);
      if(b2 == EOF)
              eof_error();
      return (u_int16_t)((b1 << 8) | b2);
}

/* Reads in a value from the constant pool. */
void skip_constant(FILE *classfile, u_int16_t *cur)
{
      u_int16_t len;
      int seekerr = 1;
      pool[*cur] = ftell(classfile);
      switch(read_8(classfile))
      {
      case CP_UTF8:
              len = read_16(classfile);
              seekerr = fseek(classfile, len, SEEK_CUR);
              break;
      case CP_CLASS:
      case CP_STRING:
      case CP_METHODTYPE:
              seekerr = fseek(classfile, 2, SEEK_CUR);
              break;
      case CP_METHODHANDLE:
              seekerr = fseek(classfile, 3, SEEK_CUR);
              break;
      case CP_INTEGER:
      case CP_FLOAT:
      case CP_FIELDREF:
      case CP_METHODREF:
      case CP_INTERFACEMETHODREF:
      case CP_NAMEANDTYPE:
      case CP_INVOKEDYNAMIC:
              seekerr = fseek(classfile, 4, SEEK_CUR);
              break;
      case CP_LONG:
      case CP_DOUBLE:
              seekerr = fseek(classfile, 8, SEEK_CUR);
              ++(*cur);
              break;
      default:
              corrupt_error();
      }
      if(seekerr)
              seek_error();
}

void error(const char *format, ...)
{
      va_list ap;
      va_start(ap, format);
      vfprintf(stderr, format, ap);
      va_end(ap);
      exit(1);
}

int main(int argc, char **argv)
{
      FILE *classfile;
      u_int16_t cp_count, i, this_class, classinfo_ptr;
      u_int8_t length;

      program = argv[0];

      if(!argv[1])
              error("%s: Missing input file\n", program);
      classfile = fopen(argv[1], "rb");
      if(!classfile)
              error("%s: Error opening %s\n", program, argv[1]);

      if(fseek(classfile, 8, SEEK_SET))  /* skip magic and version numbers */
              seek_error();
      cp_count = read_16(classfile);
      pool = calloc(cp_count, sizeof(long));
      if(!pool)
              error("%s: Out of memory for constant pool\n", program);

      for(i = 1; i < cp_count; ++i)
              skip_constant(classfile, &i);
      if(fseek(classfile, 2, SEEK_CUR))       /* skip access flags */
              seek_error();

      this_class = read_16(classfile);
      if(this_class < 1 || this_class >= cp_count)
              corrupt_error();
      if(!pool[this_class] || pool[this_class] == -1)
              corrupt_error();
      if(fseek(classfile, pool[this_class] + 1, SEEK_SET))
              seek_error();

      classinfo_ptr = read_16(classfile);
      if(classinfo_ptr < 1 || classinfo_ptr >= cp_count)
              corrupt_error();
      if(!pool[classinfo_ptr] || pool[classinfo_ptr] == -1)
              corrupt_error();
      if(fseek(classfile, pool[classinfo_ptr] + 1, SEEK_SET))
              seek_error();

      length = read_16(classfile);
      for(i = 0; i < length; ++i)
      {
              u_int8_t x = read_8(classfile);
              if((x & 0x80) || !x)
              {
                      if((x & 0xE0) == 0xC0)
                      {
                              u_int8_t y = read_8(classfile);
                              if((y & 0xC0) == 0x80)
                              {
                                      int c = ((x & 0x1f) << 6) + (y & 0x3f);
                                      if(c) putchar(c);
                                      else utf8_error();
                              }
                              else utf8_error();
                      }
                      else utf8_error();
              }
              else if(x == '/') putchar('.');
              else putchar(x);
      }
      putchar('\n');
      free(pool);
      fclose(classfile);
      return 0;
}

jarwrapper

#!/bin/bash
# /usr/local/java/bin/jarwrapper - the wrapper for binfmt_misc/jar

java -jar $1

现在只需 chmod +x 要执行的 .class.jar 和/或 .html 文件。

要将 Java 程序添加到您的路径,最好在 /usr/bin(或您喜欢的其他位置)中创建一个指向主 .class 文件的符号链接,省略 .class 扩展名。包含原始 .class 文件的目录将在执行期间添加到您的 CLASSPATH。

要测试您的新设置,请在以下简单的 Java 应用程序中输入,并将其命名为“HelloWorld.java”

class HelloWorld {
        public static void main(String args[]) {
                System.out.println("Hello World!");
        }
}

现在使用以下命令编译应用程序

javac HelloWorld.java

使用以下命令设置二进制文件的执行权限

chmod 755 HelloWorld.class

然后执行它

./HelloWorld.class

要执行 Java Jar 文件,只需使用 chmod 命令为 *.jar 文件添加执行位,然后执行

./Application.jar

要执行 Java Applet,只需使用 chmod 命令为 *.html 文件添加执行位,然后执行

./Applet.html

最初由 Brian A. Lantz, brian@lantz.com 编写,由 Richard Günther 为 binfmt_misc 大量编辑,Colin J. Watson <cjw44@cam.ac.uk> 添加了新脚本,Kurt Huwig <kurt@iku-netz.de> 添加了可执行 Jar 文件支持