处理 Java 字节码中的 Try/Catch 异常? (“堆叠高度不一致”)

发布于 2024-12-21 00:17:34 字数 885 浏览 0 评论 0原文

我正在尝试在 java 字节码中进行一些错误处理。我首先尝试实现一些类似 catch 的子例程,在其中我会检查错误条件,然后跳转到适当的子例程,有点像:

  iconst_1
  iconst_0
  dup
  ifeq calldiverr
  goto enddivtest
calldiverr:
  jsr divError
enddivtest:
  idiv

...More instructions...

divError:
  getstatic java/lang/System/out Ljava/io/PrintStream;
  ldc "Oh dear you divided by 0!"
  invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V

上面的问题是,当我有多个指令跳转到这个子例程时,我运行字节码时出现错误信息,说堆栈高度不一致。

也许使用异常是解决这个问题的最佳方法?

通过一些谷歌搜索,我发现您可以创建 Exception 类的实例并使用以下内容初始化它们:

new java/lang/Exception
dup
ldc "exception message!"
invokespecial java/lang/Exception/<init>(Ljava/lang/String;)V

我还发现您可以使用 athrow 抛出它们,这看起来没问题。

然而,让我困惑的是如何捕获异常。似乎有一个神奇的“异常表”,它将异常的抛出和捕获粘合在一起,但我不知道在从头开始编写字节码(并使用 Jasmin 组装)时如何定义其中之一。有人可以告诉我创建异常表的秘密吗?并可能给我一个将与 jasmin 一起组装的异常处理示例?

I am trying to do some error handling in java bytecode. I first tried to implement some catch-like subroutines, where I would check for the error condition, and jump to the appropriate subroutine, a little like:

  iconst_1
  iconst_0
  dup
  ifeq calldiverr
  goto enddivtest
calldiverr:
  jsr divError
enddivtest:
  idiv

...More instructions...

divError:
  getstatic java/lang/System/out Ljava/io/PrintStream;
  ldc "Oh dear you divided by 0!"
  invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V

The problem with the above is that when I have multiple instructions that jump to this subroutine, I get an error message when running the bytecode, saying that the stack height is inconsistent.

Perhaps using exceptions is the best way to get around this?

From some googling I have found that you can create instances of Exception classes and initialise them with something like:

new java/lang/Exception
dup
ldc "exception message!"
invokespecial java/lang/Exception/<init>(Ljava/lang/String;)V

I have also found that you can throw them with athrow and this seems ok.

What is confusing me however is exactly how exceptions are caught. There seems to be a magical "Exception table" which glues the throwing and catching of exceptions together, but I do not know how to define one of these when writing bytecode from scratch (and assembling using Jasmin). Can somebody tell me the secret of creating an exception table? And possibly give me an example of exception handling that will assemble with jasmin?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

第几種人 2024-12-28 00:17:34

最后我想出了一个比jsr更好的解决方案——在Jasmin中使用.method定义一个方法。我只是在检测到错误后使用 invokestatic 来调用我的错误处理程序。

对于那些寻找实际异常处理的人 - 我认为在 Jasmin 中定义异常表可以使用 .catch 来完成,但我还没有研究它,因为方法定义解决了我的问题。

编辑:

我最终不得不查看 .catch ,发现它真的很容易使用。它记录在此处

In the end I came up with a better solution than jsr - defining a method using .method in Jasmin. I just used invokestatic to call my error handler once I had detected the error.

For those looking for actual exception handling - I think that defining the exception table in Jasmin may be done using .catch, but I haven't looked into it as the method definition solved my problem.

Edit:

I did have to look at .catch in the end, and found it is really easy to use. It's documented here.

舞袖。长 2024-12-28 00:17:34

首先值得指出的是,51.0 版本中的类文件可能不包含 jsr 指令。重复代码或使用方法。

在字节码中的每一点,帧中每个元素的静态类型都必须已知。每个帧不是一个调用堆栈。

一般来说,你不想玩大筹码。将临时变量存储在局部变量中以使事情变得简单。

如果抛出异常,那么显然该框架可能具有来自异常可能抛出的任何位置的内容。因此内容被丢弃并被异常替换。无论如何,您将无法返回并继续使用框架内容。

First off its worth pointing out that class files from version 51.0 may not contain the jsr instruction. Repeat the code or use a method.

At every point in bytecode, the static type of every element in the frame must be known. Each frame is not a call stack.

Generally you don't want to play around with huge big stacks. Store temporaries in local variables to keep things simple.

If an exception is thrown then obviously the frame might have had the contents from any place the exception could have thrown. So the contents are discarded and replaced by the exception. You wouldn't be able to go back and resume to use the frame contents anyway.

任性一次 2024-12-28 00:17:34

验证 jsr 的规则非常复杂,正如 Tom 指出的那样,操作码已被弃用。所以最好避免。

我对 jsr 的记忆有点模糊,但是……

(已更新)Java 字节码验证中有一条规则,无论两个控制流连接在一起,两个分支的堆栈深度都必须相同。 jsr 子例程在某种程度上不受此规则的约束 - 具有不同堆栈深度的多个异常点可以“到达”相同的 jsr 例程,但堆栈深度的净变化为后续 ret 的 jsr 例程入口必须为零(或实际上负 1,因为异常原因始终在例程入口处推送)。

此外,虽然 jsr 例程可以“转义”并分支回常规控制流,但如果这样做,jsr 也不能免除连接点的堆栈深度规则。这严重限制了您可以执行此操作的情况,因为 jsr 例程可能会以不同的堆栈深度进入。

(毫无疑问,我仍然犯了一些错误,但这是我能做的最好的事情。)

(我不太明白你打算如何“解决”你的 jsr 问题 (也有例外。)

(此外,Sun 使用 4 或 5(不记得是哪一个)使字节码编写变得更加复杂,使得手动编码字节码几乎不可能。他们这样做是因为他们不知道如何进行验证速度足以击败 IBM否则验证者,但那是另一回事。)

The rules for verifying jsr are pretty complicated, and as Tom indicated the opcode is deprecated. So it's best avoided.

My memory on jsr is a bit fuzzy, but ...

(Updated) There is a rule in Java bytecode verification that wherever two control flows join together the stack depth must be identical along both branches. jsr subroutines are exempted from this rule to a point -- multiple exception points with different stack depths can "reach" the same jsr routine, but the net change in stack depth from jsr routine entry to subsequent ret must be zero (or actually minus 1, since the exception cause is always pushed on entry to the routine).

Further, while a jsr routine can "escape" and branch back into regular control flow, if it does so the jsr is not exempted from the stack depth rule for join points. This severely limits the situations where you can do this, since a jsr routine can potentially be entered with different stack depths.

(And I've no doubt still got some of that wrong, but it's the best I can do.)

(I don't quite understand how you're planning to "get around" your jsr problem with exceptions.)

(Also, Sun made bytecode writing much more complicated with 4 or 5 (can't remember which), making it pretty much impossible to hand-code bytecodes. They did this because they didn't know how to do verification fast enough to beat IBM's verifier otherwise, but that's another matter.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文