SIGSEGV при записи в, но не чтение из области памяти в golang - PullRequest
0 голосов
/ 02 марта 2019

У меня сложилось впечатление, что использование пакета unsafe позволяет читать / записывать произвольные данные.Я пытаюсь изменить значение, на которое указывает interface{}, не меняя сам указатель.

Предполагается, что interface{} реализован как

type _interface struct {
    type_info *typ
    value unsafe.Pointer
}

. Сбой при использовании SIGSEGV, хотячтение успешно.

func data(i interface{}) unsafe.Pointer {
    return unsafe.Pointer((*((*[2]uintptr)(unsafe.Pointer(&i))))[1])
}

func main() {
    var i interface{}
    i = 2
    fmt.Printf("%v, %v\n", (*int)(data(i)), *(*int)(data(i)))
    *((*int)(data(i))) = 3
}

Я что-то не так делаю, или это невозможно на Голанге?

1 Ответ

0 голосов
/ 05 марта 2019

Хм ... Вот как я понимаю ваш второй пример кода в настоящее время, на случай, если я допустил ошибку (если вы заметили что-то не так в том, что я описываю, мой ответ, вероятно, неверно неверен, и вы должны игнорировать остальныеиз того, что я должен сказать).

  1. Выделите память для интерфейса i в main.
  2. Установите значение i целочисленного типа со значением 2.
  3. Выделить память для интерфейса i в data.
  4. Скопировать значение основного i в данные i;то есть установите значение нового интерфейса в целочисленный тип со значением 2.
  5. Преобразуйте адрес новой переменной в указатель на массив длины 2 из uintptr (с обслуживанием unsafe.Pointerкак посредник, который заставляет компилятор принять это приведение).
  6. Приведение второго элемента массива (значение которого является адресом части значения i в data) обратно в unsafe.Pointer и верните его.

Я сделал попытку сделать то же самое в несколько шагов, но, к сожалению, я столкнулся с теми же проблемами: программа распознает, что яимеет не ноль указатель, и он может разыменовать указатель для чтения, но использование того же указателя для записи приводит к ошибке времени выполнения.

Это шаг 6, на который go vet жалуется, и я думаю, что это потому, чтосогласно пакет документов,

uintptr является целым числом, а не ссылкой.Преобразование указателя в uintptr создает целочисленное значение без семантики указателя.Даже если uintptr содержит адрес какого-либо объекта, сборщик мусора не будет обновлять значение этого uintptr, если объект перемещается, и этот uintptr не будет препятствовать восстановлению объекта.

Более конкретно,из того, что я могу сказать (хотя я признаю, что у меня возникают проблемы с поиском явного подтверждения без сканирования компилятора и источника времени выполнения), среда выполнения, кажется, не отслеживает часть значения типа interface{} как дискретнуюуказатель с собственным счетчиком ссылок;Вы, конечно, можете растоптать оба слова interface{}, записав другое значение интерфейса в целом, но это совсем не то, что вы хотели сделать (напишите вадрес памяти указателя, который находится внутри типа интерфейса, все без перемещения указателя).

Интересно то, что мы, похоже, можем приблизить это поведение , просто определив наше собственноеструктурированный тип, который не с учетом особой обработки компилятором (интерфейсы явно несколько особенные, с синтаксисом утверждения типа и всем).То есть мы можем использовать unsafe.Pointer для сохранения ссылки, указывающей на конкретную точку в памяти, и независимо от того, к чему мы ее приводим, адрес памяти никогда не перемещается, даже если значение изменяется (и значение может быть переосмыслено при помощи приведенияэто к чему-то еще).Часть, которая удивляет меня немного, состоит в том, что, по крайней мере, в моем собственном примере, и, по крайней мере, в среде Playground, указанное значение не имеет фиксированного размера;мы можем установить адрес для записи в один раз , и повторные записи в этот адрес будут успешными даже при огромных (или крошечных) объемах данных.

Конечно, по крайней мере с этой реализацией мыпотерять кучу других полезных вещей, которые мы ассоциируем с типами интерфейса, особенно с непустыми типами интерфейса (то есть с методами).Таким образом, нет никакого способа использовать это, чтобы (например) сделать супер-хитрый «универсальный» тип.Кажется, что интерфейс является его собственным значением, и часть определения этого значения является адресом в памяти, но это не совсем то же самое, что указатель.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...