1

So I decided that the "easiest" way to answer my other question is to "simply" look at the implementation of the unsafe in C/C++.

(

In a nutshell, I have an Object base and a long offset in Java, that I'm passing to C/C++ by means of the Java Native Interface (JNI), from where I want to execute the C/C++ equivalent of

unsafe.setInt(base,offset,1)

)

Such an implementation is provided, for example, by the OpenJDK: unsafe.cpp

I don't care about most that's in there, I just want a direct access to values.

So looking at the implementation, I see

#define SET_FIELD(obj, offset, type_name, x) \
  oop p = JNIHandles::resolve(obj); \
  *(type_name*)index_oop_from_field_offset_long(p, offset) = x

Which I hope does what I expect it to do.

So looking at inline oop JNIHandles::resolve(jobject handle) in jniHandles.hpp, the first line translates to

oop p = (handle == NULL ? (oop)NULL : *(oop*)handle);

The second line is casting the address to the proper pointer type and writing the value, so index_oop_from_field_offset_long(p,offset) must be what produces the address. This one translates to

if (sizeof(char*) == sizeof(jint))    // (this constant folds!)
    return (address)p + (jint) byte_offset;
else
    return (address)p +        byte_offset;

where byte_offset == offset.

Combining the two, I believe this is the way to get the proper address (which I will have to cast to the proper type when reading/writing):

inline void* get_addr(jobject base, jlong offset){
    oop p = (base == NULL ? (oop)NULL : *(oop*)base);
    if (sizeof(char*) == sizeof(jint))
        return (address)p + (jint) offset;
    else
        return (address)p +        offset;

}

Great, now all I need to know is how to choose oop and address s.t. the address is properly created.

I don't necessarily need to care about what they are, exactly, I just need to do a

using oop = SOMETHING_MATCHING_OOP_IN_SIZE;
using address = SOMETHING_ELSE_THAT_MATCHES_ADDRESS;

But what? I've flipped through the files until I grew tired of missing what I'm looking for and downloaded the hotspot source files (links at the bottom of the navigation on the left side).

grep -r "typedef .* oop" .

executed from within the src folder reveals

./share/vm/memory/classify.hpp:typedef enum oop_type {
./share/vm/oops/oopsHierarchy.hpp:typedef juint narrowOop; // Offset instead of address for an oop within a java object
./share/vm/oops/oopsHierarchy.hpp:typedef class klassOopDesc* wideKlassOop; // to keep SA happy and unhandled oop
./share/vm/oops/oopsHierarchy.hpp:typedef class oopDesc*    oop;

so grep -r ".*class oopDesc.*" .

gives me

./share/vm/gc_implementation/parallelScavenge/parMarkBitMap.hpp:class oopDesc;
./share/vm/interpreter/bytecodeInterpreter.hpp:    class oopDesc*   r;
./share/vm/memory/universe.hpp:  friend class oopDesc;
./share/vm/oops/oop.hpp:class oopDesc {
./share/vm/oops/oopsHierarchy.hpp:typedef class oopDesc*    oop;

Opening oop.hpp, we find a 353 line class definition that I have no idea yet how to translate into a useful figure -- especially since at least on of the fields may or may not be there, depending on an #ifndef

There HAS to be an easier way to go about this. But I don't immediately see how. Any suggestions?

User1291
  • 7,664
  • 8
  • 51
  • 108
  • 2
    First of all, there is no language called "C/C++". They are separate languages with different rules about casting and aliasing. For example, C++ says that casting from a valid `X*` to `void*` and back again will give you the same `X*` value. But if you instead cast the `void*` to `Y*`, you are on your own. Looks like that's what you are doing here. Also, you look at implementation details for a particular VM. From that I wouldn't draw any conclusions about how this would work on other VMs. – Bo Persson Nov 13 '17 at 15:51
  • @BoPersson Thank you for your input. Let's assume I want it to work on this particular VM. And I'm using C/C++ because JNI supports either and I quite simply don't care which I have to use so long as it works in the end. – User1291 Nov 13 '17 at 15:53
  • I don't know enough Java to be of much help. I'm here because of the C++ tag. :-) Anyway, I would consider possibly getting the value back to Java and do the update there. And also, in "other VMs" I include the next full release of this VM. – Bo Persson Nov 13 '17 at 15:57
  • @BoPersson Well', it's mostly C++ code that's used for the code I'm referring to. And the question is essentially a C++ problem. There are two problems with getting the value back to Java and updating it there: One, it's too slow. Two, if you follow the "my other question" link at the beginning of the post, you'll see that I eventually intend to use this within an RTM transaction. RTM transactions abort, at which point they jump to an abort label. So we'd have to do abort handling in java and somehow make a (very expensive) callback to that handler from within C++ at every abort. – User1291 Nov 13 '17 at 16:01
  • *And I'm using C/C++ because JNI supports either...* JNI only really supports C - note well that to get your C++ code called via JNI, there's the `extern "C"` declaration. You can get away with using C++ almost all the time because the C++ runtime linked into the JVM process doesn't usually break anything - [until it does](https://stackoverflow.com/questions/47184167/jni-code-crash-with-core-dump-when-throw-is-called-under-64-bit). – Andrew Henle Nov 13 '17 at 17:01

0 Answers0