liyujie
2025-08-28 d9927380ed7c8366f762049be9f3fee225860833
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
/* Copyright (C) 2016 The Android Open Source Project
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This file implements interfaces from the file jvmti.h. This implementation
 * is licensed under the same terms as the file jvmti.h.  The
 * copyright and license information for the file jvmti.h follows.
 *
 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
 
#include "ti_class_definition.h"
 
#include "base/array_slice.h"
#include "base/logging.h"
#include "class_linker-inl.h"
#include "class_root.h"
#include "dex/dex_file.h"
#include "fixed_up_dex_file.h"
#include "handle.h"
#include "handle_scope-inl.h"
#include "mirror/class-inl.h"
#include "mirror/class_ext-inl.h"
#include "mirror/object-inl.h"
#include "reflection.h"
#include "thread.h"
 
namespace openjdkjvmti {
 
void ArtClassDefinition::InitializeMemory() const {
  DCHECK(art::MemMap::kCanReplaceMapping);
  VLOG(signals) << "Initializing de-quickened memory for dex file of " << name_;
  CHECK(dex_data_mmap_.IsValid());
  CHECK(temp_mmap_.IsValid());
  CHECK_EQ(dex_data_mmap_.GetProtect(), PROT_NONE);
  CHECK_EQ(temp_mmap_.GetProtect(), PROT_READ | PROT_WRITE);
 
  std::string desc = std::string("L") + name_ + ";";
  std::unique_ptr<FixedUpDexFile>
      fixed_dex_file(FixedUpDexFile::Create(*initial_dex_file_unquickened_, desc.c_str()));
  CHECK(fixed_dex_file.get() != nullptr);
  CHECK_LE(fixed_dex_file->Size(), temp_mmap_.Size());
  CHECK_EQ(temp_mmap_.Size(), dex_data_mmap_.Size());
  // Copy the data to the temp mmap.
  memcpy(temp_mmap_.Begin(), fixed_dex_file->Begin(), fixed_dex_file->Size());
 
  // Move the mmap atomically.
  art::MemMap source;
  source.swap(temp_mmap_);
  std::string error;
  CHECK(dex_data_mmap_.ReplaceWith(&source, &error)) << "Failed to replace mmap for "
                                                     << name_ << " because " << error;
  CHECK(dex_data_mmap_.Protect(PROT_READ));
}
 
bool ArtClassDefinition::IsModified() const {
  // RedefineClasses calls always are 'modified' since they need to change the current_dex_file of
  // the class.
  if (redefined_) {
    return true;
  }
 
  // Check to see if any change has taken place.
  if (current_dex_file_.data() == dex_data_.data()) {
    // no change at all.
    return false;
  }
 
  // The dex_data_ was never touched by the agents.
  if (dex_data_mmap_.IsValid() && dex_data_mmap_.GetProtect() == PROT_NONE) {
    if (current_dex_file_.data() == dex_data_mmap_.Begin()) {
      // the dex_data_ looks like it changed (not equal to current_dex_file_) but we never
      // initialized the dex_data_mmap_. This means the new_dex_data was filled in without looking
      // at the initial dex_data_.
      return true;
    } else if (dex_data_.data() == dex_data_mmap_.Begin()) {
      // The dex file used to have modifications but they were not added again.
      return true;
    } else {
      // It's not clear what happened. It's possible that the agent got the current dex file data
      // from some other source so we need to initialize everything to see if it is the same.
      VLOG(signals) << "Lazy dex file for " << name_ << " was never touched but the dex_data_ is "
                    << "changed! Need to initialize the memory to see if anything changed";
      InitializeMemory();
    }
  }
 
  // We can definitely read current_dex_file_ and dex_file_ without causing page faults.
 
  // Check if the dex file we want to set is the same as the current one.
  // Unfortunately we need to do this check even if no modifications have been done since it could
  // be that agents were removed in the mean-time so we still have a different dex file. The dex
  // checksum means this is likely to be fairly fast.
  return current_dex_file_.size() != dex_data_.size() ||
      memcmp(current_dex_file_.data(), dex_data_.data(), current_dex_file_.size()) != 0;
}
 
jvmtiError ArtClassDefinition::InitCommon(art::Thread* self, jclass klass) {
  art::ScopedObjectAccess soa(self);
  art::ObjPtr<art::mirror::Class> m_klass(soa.Decode<art::mirror::Class>(klass));
  if (m_klass.IsNull()) {
    return ERR(INVALID_CLASS);
  }
  initialized_ = true;
  klass_ = klass;
  loader_ = soa.AddLocalReference<jobject>(m_klass->GetClassLoader());
  std::string descriptor_store;
  std::string descriptor(m_klass->GetDescriptor(&descriptor_store));
  name_ = descriptor.substr(1, descriptor.size() - 2);
  // Android doesn't really have protection domains.
  protection_domain_ = nullptr;
  return OK;
}
 
static void DequickenDexFile(const art::DexFile* dex_file,
                             const char* descriptor,
                             /*out*/std::vector<unsigned char>* dex_data)
    REQUIRES_SHARED(art::Locks::mutator_lock_) {
  std::unique_ptr<FixedUpDexFile> fixed_dex_file(
      FixedUpDexFile::Create(*dex_file, descriptor));
  dex_data->resize(fixed_dex_file->Size());
  memcpy(dex_data->data(), fixed_dex_file->Begin(), fixed_dex_file->Size());
}
 
// Gets the data surrounding the given class.
static void GetDexDataForRetransformation(art::Handle<art::mirror::Class> klass,
                                          /*out*/std::vector<unsigned char>* dex_data)
    REQUIRES_SHARED(art::Locks::mutator_lock_) {
  art::StackHandleScope<3> hs(art::Thread::Current());
  art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->GetExtData()));
  const art::DexFile* dex_file = nullptr;
  if (!ext.IsNull()) {
    art::Handle<art::mirror::Object> orig_dex(hs.NewHandle(ext->GetOriginalDexFile()));
    if (!orig_dex.IsNull()) {
      if (orig_dex->IsArrayInstance()) {
        DCHECK(orig_dex->GetClass()->GetComponentType()->IsPrimitiveByte());
        art::Handle<art::mirror::ByteArray> orig_dex_bytes(hs.NewHandle(orig_dex->AsByteArray()));
        dex_data->resize(orig_dex_bytes->GetLength());
        memcpy(dex_data->data(), orig_dex_bytes->GetData(), dex_data->size());
        return;
      } else if (orig_dex->IsDexCache()) {
        dex_file = orig_dex->AsDexCache()->GetDexFile();
      } else {
        DCHECK(orig_dex->GetClass()->DescriptorEquals("Ljava/lang/Long;"))
            << "Expected java/lang/Long but found object of type "
            << orig_dex->GetClass()->PrettyClass();
        art::ObjPtr<art::mirror::Class> prim_long_class(
            art::GetClassRoot(art::ClassRoot::kPrimitiveLong));
        art::JValue val;
        if (!art::UnboxPrimitiveForResult(orig_dex.Get(), prim_long_class, &val)) {
          // This should never happen.
          LOG(FATAL) << "Unable to unbox a primitive long value!";
        }
        dex_file = reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(val.GetJ()));
      }
    }
  }
  if (dex_file == nullptr) {
    dex_file = &klass->GetDexFile();
  }
  std::string storage;
  DequickenDexFile(dex_file, klass->GetDescriptor(&storage), dex_data);
}
 
static bool DexNeedsDequickening(art::Handle<art::mirror::Class> klass,
                                 /*out*/ bool* from_class_ext)
    REQUIRES_SHARED(art::Locks::mutator_lock_) {
  art::ObjPtr<art::mirror::ClassExt> ext(klass->GetExtData());
  if (ext.IsNull()) {
    // We don't seem to have ever been redefined so be conservative and say we need de-quickening.
    *from_class_ext = false;
    return true;
  }
  art::ObjPtr<art::mirror::Object> orig_dex(ext->GetOriginalDexFile());
  if (orig_dex.IsNull()) {
    // We don't seem to have ever been redefined so be conservative and say we need de-quickening.
    *from_class_ext = false;
    return true;
  } else if (!orig_dex->IsArrayInstance()) {
    // We were redefined but the original is held in a dex-cache or dex file. This means that the
    // original dex file is the one from the disk, which might be quickened.
    DCHECK(orig_dex->IsDexCache() || orig_dex->GetClass()->DescriptorEquals("Ljava/lang/Long;"));
    *from_class_ext = true;
    return true;
  } else {
    // An array instance means the original-dex-file is from a redefineClasses which cannot have any
    // quickening, so it's fine to use directly.
    DCHECK(orig_dex->GetClass()->GetComponentType()->IsPrimitiveByte());
    *from_class_ext = true;
    return false;
  }
}
 
static const art::DexFile* GetQuickenedDexFile(art::Handle<art::mirror::Class> klass)
    REQUIRES_SHARED(art::Locks::mutator_lock_) {
  art::ObjPtr<art::mirror::ClassExt> ext(klass->GetExtData());
  if (ext.IsNull() || ext->GetOriginalDexFile() == nullptr) {
    return &klass->GetDexFile();
  }
 
  art::ObjPtr<art::mirror::Object> orig_dex(ext->GetOriginalDexFile());
  DCHECK(!orig_dex->IsArrayInstance());
  if (orig_dex->IsDexCache()) {
    return orig_dex->AsDexCache()->GetDexFile();
  }
 
  DCHECK(orig_dex->GetClass()->DescriptorEquals("Ljava/lang/Long;"))
      << "Expected java/lang/Long but found object of type "
      << orig_dex->GetClass()->PrettyClass();
  art::ObjPtr<art::mirror::Class> prim_long_class(
      art::GetClassRoot(art::ClassRoot::kPrimitiveLong));
  art::JValue val;
  if (!art::UnboxPrimitiveForResult(orig_dex.Ptr(), prim_long_class, &val)) {
    LOG(FATAL) << "Unable to unwrap a long value!";
  }
  return reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(val.GetJ()));
}
 
template<typename GetOriginalDexFile>
void ArtClassDefinition::InitWithDex(GetOriginalDexFile get_original,
                                     const art::DexFile* quick_dex) {
  art::Thread* self = art::Thread::Current();
  DCHECK(quick_dex != nullptr);
  if (art::MemMap::kCanReplaceMapping && kEnableOnDemandDexDequicken) {
    size_t dequick_size = quick_dex->GetDequickenedSize();
    std::string mmap_name("anon-mmap-for-redefine: ");
    mmap_name += name_;
    std::string error;
    dex_data_mmap_ = art::MemMap::MapAnonymous(mmap_name.c_str(),
                                               dequick_size,
                                               PROT_NONE,
                                               /*low_4gb=*/ false,
                                               &error);
    mmap_name += "-TEMP";
    temp_mmap_ = art::MemMap::MapAnonymous(mmap_name.c_str(),
                                           dequick_size,
                                           PROT_READ | PROT_WRITE,
                                           /*low_4gb=*/ false,
                                           &error);
    if (UNLIKELY(dex_data_mmap_.IsValid() && temp_mmap_.IsValid())) {
      // Need to save the initial dexfile so we don't need to search for it in the fault-handler.
      initial_dex_file_unquickened_ = quick_dex;
      dex_data_ = art::ArrayRef<const unsigned char>(dex_data_mmap_.Begin(),
                                                     dex_data_mmap_.Size());
      if (from_class_ext_) {
        // We got initial from class_ext so the current one must have undergone redefinition so no
        // cdex or quickening stuff.
        // We can only do this if it's not a first load.
        DCHECK(klass_ != nullptr);
        const art::DexFile& cur_dex = self->DecodeJObject(klass_)->AsClass()->GetDexFile();
        current_dex_file_ = art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.Size());
      } else {
        // This class hasn't been redefined before. The dequickened current data is the same as the
        // dex_data_mmap_ when it's filled it. We don't need to copy anything because the mmap will
        // not be cleared until after everything is done.
        current_dex_file_ = art::ArrayRef<const unsigned char>(dex_data_mmap_.Begin(),
                                                               dequick_size);
      }
      return;
    }
  }
  dex_data_mmap_.Reset();
  temp_mmap_.Reset();
  // Failed to mmap a large enough area (or on-demand dequickening was disabled). This is
  // unfortunate. Since currently the size is just a guess though we might as well try to do it
  // manually.
  get_original(/*out*/&dex_data_memory_);
  dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
  if (from_class_ext_) {
    // We got initial from class_ext so the current one must have undergone redefinition so no
    // cdex or quickening stuff.
    // We can only do this if it's not a first load.
    DCHECK(klass_ != nullptr);
    const art::DexFile& cur_dex = self->DecodeJObject(klass_)->AsClass()->GetDexFile();
    current_dex_file_ = art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.Size());
  } else {
    // No redefinition must have ever happened so the (dequickened) cur_dex is the same as the
    // initial dex_data. We need to copy it into another buffer to keep it around if we have a
    // real redefinition.
    current_dex_memory_.resize(dex_data_.size());
    memcpy(current_dex_memory_.data(), dex_data_.data(), current_dex_memory_.size());
    current_dex_file_ = art::ArrayRef<const unsigned char>(current_dex_memory_);
  }
}
 
jvmtiError ArtClassDefinition::Init(art::Thread* self, jclass klass) {
  jvmtiError res = InitCommon(self, klass);
  if (res != OK) {
    return res;
  }
  art::ScopedObjectAccess soa(self);
  art::StackHandleScope<1> hs(self);
  art::Handle<art::mirror::Class> m_klass(hs.NewHandle(self->DecodeJObject(klass)->AsClass()));
  if (!DexNeedsDequickening(m_klass, &from_class_ext_)) {
    // We don't need to do any dequickening. We want to copy the data just so we don't need to deal
    // with the GC moving it around.
    art::ObjPtr<art::mirror::ByteArray> orig_dex(
        m_klass->GetExtData()->GetOriginalDexFile()->AsByteArray());
    dex_data_memory_.resize(orig_dex->GetLength());
    memcpy(dex_data_memory_.data(), orig_dex->GetData(), dex_data_memory_.size());
    dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
 
    // Since we are here we must not have any quickened instructions since we were redefined.
    const art::DexFile& cur_dex = m_klass->GetDexFile();
    DCHECK(from_class_ext_);
    current_dex_file_ = art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.Size());
    return OK;
  }
 
  // We need to dequicken stuff. This is often super slow (10's of ms). Instead we will do it
  // dynamically.
  const art::DexFile* quick_dex = GetQuickenedDexFile(m_klass);
  auto get_original = [&](/*out*/std::vector<unsigned char>* dex_data)
      REQUIRES_SHARED(art::Locks::mutator_lock_) {
    GetDexDataForRetransformation(m_klass, dex_data);
  };
  InitWithDex(get_original, quick_dex);
  return OK;
}
 
jvmtiError ArtClassDefinition::Init(art::Thread* self, const jvmtiClassDefinition& def) {
  jvmtiError res = InitCommon(self, def.klass);
  if (res != OK) {
    return res;
  }
  // We are being directly redefined.
  redefined_ = true;
  current_dex_file_ = art::ArrayRef<const unsigned char>(def.class_bytes, def.class_byte_count);
  dex_data_ = art::ArrayRef<const unsigned char>(def.class_bytes, def.class_byte_count);
  return OK;
}
 
void ArtClassDefinition::InitFirstLoad(const char* descriptor,
                                       art::Handle<art::mirror::ClassLoader> klass_loader,
                                       const art::DexFile& dex_file) {
  art::Thread* self = art::Thread::Current();
  art::ScopedObjectAccess soa(self);
  initialized_ = true;
  // No Class
  klass_ = nullptr;
  loader_ = soa.AddLocalReference<jobject>(klass_loader.Get());
  std::string descriptor_str(descriptor);
  name_ = descriptor_str.substr(1, descriptor_str.size() - 2);
  // Android doesn't really have protection domains.
  protection_domain_ = nullptr;
  auto get_original = [&](/*out*/std::vector<unsigned char>* dex_data)
      REQUIRES_SHARED(art::Locks::mutator_lock_) {
    DequickenDexFile(&dex_file, descriptor, dex_data);
  };
  InitWithDex(get_original, &dex_file);
}
 
}  // namespace openjdkjvmti