Отображение структуры внутри объединения в JNA
Я пытаюсь отобразить библиотеку kstat в Solaris 11.3 на Java с помощью JNA. Несмотря на то, что мне удалось заставить работать большинство структур, последние 24 часа я боролся с особенно сложным союзом внутри структуры внутри союза.
Я успешно получаю указатель на kstat_named
Структура мне нужно с помощью kstat_data_lookup (). Мой код правильно извлекает большинство данных (name, data_type и неструктурные члены объединения) в этой структуре C:
typedef struct kstat_named {
char name[KSTAT_STRLEN]; /* name of counter */
uchar_t data_type; /* data type */
union {
charc[16]; /* enough for 128-bit ints */
struct {
union {
char *ptr; /* NULL-terminated string */
} addr;
uint32_t len; /* length of string */
} str;
int32_t i32;
uint32_t ui32;
int64_t i64;
uint64_t ui64;
/* These structure members are obsolete */
int32_t l;
uint32_t ul;
int64_t ll;
uint64_t ull;
} value; /* value of counter */
} kstat_named_t;
Я сопоставил это в JNA следующим образом:
class KstatNamed extends Structure {
public static class UNION extends Union {
public byte[] charc = new byte[16]; // enough for 128-bit ints
public Pointer str; // KstatNamedString
public int i32;
public int ui32;
public long i64;
public long ui64;
}
public byte[] name = new byte[KSTAT_STRLEN]; // name of counter
public byte data_type; // data type
public UNION value; // value of counter
public KstatNamed() {
super();
}
public KstatNamed(Pointer p) {
super();
this.useMemory(p);
this.read();
}
@Override
public void read() {
super.read();
switch (data_type) {
case KSTAT_DATA_CHAR:
value.setType(byte[].class);
break;
case KSTAT_DATA_STRING:
value.setType(Pointer.class);
break;
case KSTAT_DATA_INT32:
case KSTAT_DATA_UINT32:
value.setType(int.class);
break;
case KSTAT_DATA_INT64:
case KSTAT_DATA_UINT64:
value.setType(long.class);
break;
default:
break;
}
value.read();
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "name", "data_type", "value" });
}
}
Этот код работает правильно для типов int32 (KSTAT_DATA_INT32). Однако, когда тип данных KSTAT_DATA_STRING, который соответствует str
структура внутри union
У меня нет успеха в правильном получении данных.
Я отобразил вложенную структуру следующим образом:
class KstatNamedString extends Structure {
public static class UNION extends Union {
public Pointer ptr; // NULL-terminated string
}
public UNION addr;
public int len; // length of string
public KstatNamedString() {
super();
}
public KstatNamedString(Pointer p) {
super();
this.useMemory(p);
this.read();
}
@Override
public void read() {
super.read();
addr.setType(Pointer.class);
addr.read();
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "addr", "len" });
}
}
В конечном итоге я пытаюсь повторить поведение этого макроса C:
#define KSTAT_NAMED_STR_PTR(knptr) ((knptr)->value.str.addr.ptr)
Я пробовал несколько разных способов получить доступ к вышеуказанной структуре, но, похоже, никогда не читал правильные данные (len
значение в миллионах и пытается прочитать строку ptr
вызывает segfault). Я пробовал:
Pointer p = LibKstat.INSTANCE.kstat_data_lookup(ksp, name);
KstatNamed data = new KstatNamed(p);
KstatNamedString str = new KstatNamedString(data.value.str);
return str.addr.ptr.getString(0); // <--- Segfault on C side
Я также попробовал:
- Определение
KstatNamedString
как тип вместоPointer
тип - Используя различные комбинации
ByReference
как в структурах, так и в профсоюзах
Я везде гуглил, в том числе пытался, как мне показалось, многообещающим результатом, но, похоже, ничего не получалось.
Я уверен, что мне не хватает чего-то простого.
1 ответ
Использование KstatNamedString
вместо Pointer
тип.
Измените ваши основанные на указателе конструкторы следующим образом:
public KstatNamed(Pointer p) {
super(p);
this.read();
}
public KstatNamedString(Pointer p) {
super(p);
this.read();
}
и изменить addr
поле str
структура поля, чтобы быть простым Pointer
(нет необходимости в объединении битов вокруг него).
public Pointer /*UNION*/ addr;
Запустите свою JVM с -Djna.dump_memory=true
и распечатайте свой недавно инициализированный Structure
как строка Это покажет вам, как JNA интерпретирует структуру памяти структуры и как инициализируется собственная память. Это должно помочь вам определить, как извлечь искомую строку (если она там есть).
Вы также можете настроить свой союз read()
метод для первоначального чтения только поля типа (используя Structure.readField("data_type")
) перед установкой типа объединения.