问题标签 [jvm-bytecode]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
java - 如何创建一个将当前类静态添加到堆栈的 ASM LdcInsnNode?
我正在使用ASM 库来修改其他人创建的字节码。对于任意类中的任意方法,我想创建一个LdcInsnNode
将当前类添加到堆栈中的方法。
例如,假设我正在转换一个名为com.example.ExampleClass
. 我想创建等效于System.out.println(ExampleClass.class.getName());
.
这似乎是一个相对简单的任务。当我使用 Eclipse Bytecode Outline 插件时,它说以下字节码是等效的:
我尝试了以下代码:
这是在 的扩展中运行的ClassNode
,因此name
指的是该ClassNode.name
字段。此InsnList
方法返回的 被插入到现有AbstractInsnNode
using之前InsnList.insertBefore(AbstractInsnNode, printClass())
。在字节码中达到这一点时,我收到一个错误,原因如下:
这显然是因为 LDC 指令添加的是 String"Lcom/example/ExampleClass;.class"
而不是实际的 class Lcom/example/ExampleClass;.class
。
有什么解决方法吗?似乎不可能直接将Class
对象添加到,LdcInsnNode
因为该类尚不存在。但是有没有办法添加一个加载Class
对象的指令?
在我的特殊情况下,调用该Object.getClass()
方法不是一种选择,因为它需要在静态上下文中工作。
java - 如何在运行时使用 JVMTI 获取类文件(规范格式)?
我正在开展一个研究项目,其中包括热点分析器的反馈。目前我正在开发一个 JVMTI 代理,它应该具有以下特性:
- 侦听任何已编译的加载事件。
- 提取并分析具有热点方法的完整类文件。
- 修改/重新定义类的字节码。
我在 JVMTI 中有很多可用的 API 函数来获取有关具有由 JIT 编译的方法的类文件的信息。但是,我想要 java 虚拟机规范中描述的方法的完整类文件。如果无法获得整个类文件,我至少想要一个以下格式的类文件:
到目前为止,我有以下代码可以部分达到目的:
我的问题是:
- 是否可以通过代理获得完整的类文件?
- 如果没有,我如何使用或API填充
ClassFile
结构?JVMTI
JNI
- 还有其他策略可以实现我的目标吗?
限制:
- 在类加载后很长一段时间内,我必须在运行时检查和操作字节码。
ASM
所以使用类似库的AFAIK java代理JAVASSIST
不会有任何帮助。
任何帮助将不胜感激。
android - Android bytecode: value of some variables not defined
I reverse an android application. While reading, I see some methods that used variables such as p1
or p2
... I don't know where can I reference those variables. For example:
The thing I don't understand is:
I don't know variables p1
p2
p3
here. Please tell me. Thanks
java - ASM 中的类编写器 COMPUTE_FRAMES
我一直在尝试通过玩 ASM 中的跳转来了解堆栈映射框架在 Java 中是如何工作的。我创建了一个简单的方法来尝试一些事情:(用 Krakatau 反汇编):
它所做的只是创建一个StringBuilder
将一些字符串与变量连接起来。
由于 L35 的 invokespecial 调用与 L10 的 invokespecial 调用具有完全相同的堆栈,因此我决定ICONST_1; IFEQ L10
使用 ASM 在 L35 之前添加一个序列。
当我拆卸时(再次使用 Krakatau),我发现结果很奇怪。ASM 计算出 L10 的堆栈帧为:
代替
正如我所料。
此外,这个类也不会通过验证,因为无法调用StringBuilder#<init>
。Top
根据 ASM 手册,Top
指的是一个未初始化的值,但从跳转位置和之前的代码来看,它似乎并没有在代码中未初始化。我不明白跳跃有什么问题。
我插入的跳转是否有问题,以某种方式使该类无法计算帧?这可能是 ASM 的 ClassWriter 的错误吗?
java - 有效的不可用常量池索引
我可以在 JVM 文档中阅读以下内容:
所有 8 字节常量在类文件的 constant_pool 表中占据两个条目。如果 CONSTANT_Long_info 或 CONSTANT_Double_info 结构是索引 n 处的 constant_pool 表中的项,则池中的下一个可用项位于索引 n+2 处。constant_pool 索引 n+1 必须有效但被视为不可用。
回想起来,让 8 字节常量占用两个常量池条目是一个糟糕的选择。
如果索引 n+1 有效,它怎么会“不可用”呢?此外,它似乎并非完全不可用,因为可以将值存储到其中:
long 或 double 类型的值占用两个连续的局部变量。这样的值只能使用较小的索引来解决。例如,存储在索引 n 处的局部变量数组中的 double 类型的值实际上占用了索引为 n 和 n+1 的局部变量;但是,不能从中加载索引 n+1 处的局部变量。可以存入。但是,这样做会使局部变量 n 的内容无效。
这是否意味着“有效”=“您可以存储到其中”和“不可用”=“您无法加载”?
java - 为 JVM 编写语言
假设我写了一种编程语言;同名,我称之为lang。
为了开始编写lang的漫长旅程,我决定从编写 lang 本身开始。我实际上无法运行它,因为没有什么可以运行自行运行的程序。
因此,我首先用Java为lang编写另一个编译器。这一次,当我完成后,我决定将其转换为字节码,并保留它。我现在有一个可以工作的编译器,它将我所有的语言代码转换成字节码。
所以我决定将我的语言自编译器插入到我刚刚用 Java 制作的编译器中。然后我将自编译器转换为字节码,并丢弃 Java 编译器。我现在有一个lang编译器,纯粹是自己写的,转换成字节码,可以使用了。
这创建了一个可靠的程序,我理解所有这些,但我的问题是,相对于 JVM 的编译器设计,如果我决定发布我的语言的更新怎么办?我该如何更新字节码?我是否只是简单地用旧版本重新编写语言的更新版本?
我问这个是因为这是我想做的。自己编写一种不存在的语言,然后通过首先在 Java 中创建编译器将其引导到 JVM。
这与使用 C++ 所做的相同。编写了带有类的 C,然后在其中编写了 C++,最后放弃了带有类的 C,而使用了自举的 C++。但是,他们到底是如何更新语言的呢?
java - java字节码中的if条件反转
考虑简单的例子
这里很简单: if val > 0
return yes
else return no
。但是在编译之后,在字节码中,这个 if 条件是相反的:
它检查:如果val <= 0
则返回no
,否则返回yes
。
首先,我认为<=
检查更便宜,并且是某种优化。但是如果我将我的初始代码更改为
它仍然会在字节码中反转:
那么,有这种行为的原因吗?可以改成直截了当吗?
java - javac 生成一个桥接方法,其中包含一个指向抽象方法的 invokespecial 指令
假设以下现实生活中的代码结构:
为 B 生成的字节码是
注意invokespecial 指令指向抽象方法Am() 的使用。在我看来,这必须在运行时根据 JVM 8 规范在invokespecial上导致 AbstractMethodError :
如果以下所有条件都为真,则令 C 为当前类的直接超类。
因此,在我们的示例中,A 将被选为 C。
要调用的实际方法由以下查找过程选择。
1) 如果 C 包含与已解析方法具有相同名称和描述符的实例方法的声明,那么它就是要调用的方法。
所以JVM会选择Am()。
但是运行时异常部分指出:
否则,如果查找过程的第 1 步、第 2 步或第 3 步选择了一个抽象方法,invokespecial 将抛出一个 AbstractMethodError。
因此调用该方法将导致错误。
我只是想知道,为什么 Java 编译器会生成这样的被判失败的字节码?
PS 我猜测上面的桥接方法根本不会被调用,因为 AB 类的最终实现者将覆盖它。但是接下来就出现了下面的问题:java生成这个桥接方法是为了什么目的?
java - 为什么java字节码“存储”后经常“加载”?
当我从一些小的java函数中读取jvm字节码时,我发现当在操作数堆栈上计算一个新的局部变量时,假设它会存储在局部变量表中,但通常它会立即加载到操作数堆栈中(就字面意义上的字节码而言)。操作不是很懂,是不是没必要操作?
java - Java字节码:局部变量表与堆栈计算
假设我们有以下类:
这个类广泛使用局部变量并且它们都是最终的。
如果我们查看为averageTemp
方法生成的字节码,我们将看到以下字节码:
有很多存储操作码。
现在,假设使用字节码生成库,我为相同的方法生成了以下字节码:
从语义上讲,与旧方法相比,这种新方法实现具有相同的含义 - 它仍然从三个传感器获取温度值,对它们进行平均并返回它。但它不是将中间值放入变量中,而是在堆栈上进行所有计算。我可以这样重写它,因为我所有的局部变量和字段都是最终的。
现在有一个问题:如果我正在做一些与字节码生成相关的魔术,并且到处都遵循这种“堆栈上的所有计算”方法(假设我所有的变量和字段都是最终的),我可能会面临哪些潜在的陷阱?
注意:我无意按照我描述的方式重写现有 Java 类的字节码。这里给出的示例类只是为了展示我想在我的字节码中实现的方法语义。