Отображение структуры внутри объединения в 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")) перед установкой типа объединения.

Другие вопросы по тегам