Распределение mmap возвращает 0xffffffffffffffff4 (не MAP_FAILED)

Пытаюсь выделить память с помощью mmap, вот код:

long long *copy;
copy = (long long*)mmap(NULL,
                       (size_t)1024,
                        PROT_READ | PROT_WRITE | PROT_EXEC,
                        MAP_PRIVATE | MAP_ANON, -1, 0);

  if (copy == MAP_FAILED) {
    fprintf(stderr, "Memory allocation failed (Process aborted)\n");
    exit(1);
  }
  printf("Pointer: %p\n", copy);

Очевидно, я проверяю, не происходит ли выделение. Когда это произойдет, я должен получить -1 из того, что я получил из справочные страницы. Дело в том, что я получаю -12, ну 0xfffffffffffffff4, так что ошибка не ловится и программа продолжается. Я подумал, может быть, это из-за приведения (long long*), но приведение не должно изменять значение указателя. Поэтому мне очень любопытно, почему это происходит и как это предотвратить.

Более странное поведение:

Я попытался напечатать файл errno. Если я использую printf("%d\n", errno);, он печатает 0, а указатель по-прежнему установлен на 0xfffffffffffffff4. Но если я использую err(errno, "%p", copy);, он печатает:

program.exe: 0x7f8130981000: Success

И теперь указатель действителен, но я не могу его использовать, потому что err завершил выполнение.


person Hadron    schedule 20.08.2017    source источник
comment
program.exe — вы на Windows? Вы используете MinGW или Cygwin или что-то еще?   -  person Jonathan Leffler    schedule 20.08.2017
comment
Нет, я вижу, как это может вводить в заблуждение, я работаю в Linux, но я указываю расширение .exe для организационных целей.   -  person Hadron    schedule 20.08.2017
comment
Ваша проблема вполне может быть связана с возвращением mmap. В C это никогда не требуется и может скрыть ошибки. Может быть, вы забыли #include <stdlib.h> ? Может быть, вы не включили все предупреждения? -Wall мог бы рассказать вам больше.   -  person Jens Gustedt    schedule 20.08.2017
comment
Этому предложению нет оправдания, но вы пробовали printf("Pointer: %p\n", (void *)copy)? (Хорошо: есть половина оправдания, если это сработает, но маловероятно, что это что-то изменит.)   -  person Jonathan Leffler    schedule 20.08.2017
comment
Вы, вероятно, получите лучшую помощь, предоставив MCVE: stackoverflow.com/help/mcve   -  person Gene    schedule 20.08.2017
comment
@Jens Gustedt переключатель -Wall включен без предупреждений   -  person Hadron    schedule 20.08.2017
comment
@JonathanLeffler Я безуспешно пробовал все разные актеры.   -  person Hadron    schedule 20.08.2017
comment
Неудивительно, что актерский состав ничего не изменил. Когда-то я работал над машиной, где это имело бы значение, но это было в начале 80-х, и машины больше не выпускались. (Я до сих пор помню огромный прирост, который мы получили, увеличив объем памяти с 1 МБ до 2 МБ!)   -  person Jonathan Leffler    schedule 20.08.2017
comment
Вы пробовали компилировать с -Wmissing-prototypes -Wstrict-prototypes -Werror -Wextra так же, как и с -Wall? Опять же, если вы включили необходимые заголовки, это ничего не изменит — если вы аккуратный программист. Если вы не включили необходимые заголовки или являетесь небрежным программистом, у вас может быть больше работы, чем вы ожидали. (Я использую эти опции все время; у меня нет шанса быть небрежным программистом — по крайней мере, не в том смысле, что я сталкиваюсь с проблемами из-за ошибок, которые выявляют эти опции.) Кроме того, что произойдет, если вместо этого вы используете warnc() из err()?   -  person Jonathan Leffler    schedule 20.08.2017
comment
@JonathanLeffler Используя предложенные вами флаги, я понял, что <err.h> не включен. Когда я включил его, произошло следующее: program.exe: 0xfffffffffffffff4: Success Значит, неявное объявление err заставило его работать? Также, как предположил Джин, я работаю над MCVE, но, похоже, я не могу воспроизвести это поведение.   -  person Hadron    schedule 20.08.2017
comment
Я настоятельно рекомендую использовать варианты прототипов — это предотвращает такие проблемы. Отсутствие включения <err.h> привело к сбою другими, более творческими способами — вы не знаете, был ли напечатанный указатель err() действительным, потому что программа завершилась. Я не видел поведения, похожего на то, что вы видите (за исключением вашего прото-MCVE), очень часто, особенно когда включены правильные заголовки и объявлены функции. В Linux (и некоторых, возможно, многих других системах) ошибка 12 — это ENOMEM. Есть ли опасность использования забавной функции отображения, которая преобразует код ошибки -1 в -errno? Становится диковинным…   -  person Jonathan Leffler    schedule 20.08.2017
comment
Это имело бы смысл, потому что я действительно наношу карту более чем на один регион. Я постараюсь посмотреть, решит ли это исправление возможной проблемы с отображением.   -  person Hadron    schedule 20.08.2017
comment
Правильным заголовком для mmap() является sys/mman.h как в соответствии с стандартом POSIX, так и справочная страница Linux. Откуда err.h?   -  person Andrew Henle    schedule 21.08.2017
comment
Чтобы использовать функцию err(), чтобы узнать, был ли вызов mmap() успешным. И это соответствует errno, но возвращаемое значение не является допустимым указателем. Все остальные включения были в порядке.   -  person Hadron    schedule 21.08.2017
comment
глобальная переменная errno недоступна, если у вас нет этого оператора: #include <errno.h>   -  person user3629249    schedule 21.08.2017
comment
при компиляции в linux всегда включайте все предупреждения. при минимальном использовании: gcc -c -ggdb -Wall -Wextra -pedantic -Wconversion -std=gnu11 myprogram.c -o myprogram.o   -  person user3629249    schedule 21.08.2017


Ответы (1)


При компиляции со всеми предупреждениями (-Wmissing-prototypes -Wstrict-prototypes -Werror -Wextra), как было предложено в комментариях, я понял, что <err.h> не было включено. Когда я включил его, «странное поведение», которое я описал в конце своего вопроса, исчезло; программа вела себя нормально, но все еще с плохим указателем, я этого не знал, но в этот момент программа работала бы, если бы я не проверил errno.

В некоторых других частях кода у меня есть написанные на ассемблере функции, которые всегда отлично работали (поэтому я думал, что ошибка не связана с ними). Но затем я вспоминаю, что некоторые из них используют %rbx для передачи информации между собой, но так уж получилось, что %rbx — это регистр, используемый для errno в gcc.

Итак, глядя на сгенерированный ассемблерный код для этой функции, я понял, что %rbx не был "защищен" компилятором. Добавление в заголовки <errno.h> исправило это, и теперь все работает нормально.

Несмотря на то, что кажется, что это работает без надлежащих включений, это, безусловно, урок, чтобы быть более осторожным и всегда использовать максимальные параметры предупреждения.

person Hadron    schedule 21.08.2017
comment
Что вы имеете в виду под не защищенным? rbx - это регистр, сохраненный вызываемым пользователем, и ваша задача - сохранить и восстановить его, если вы используете его в рукописной сборке. Вы должны знать это, прежде чем писать ассемблер вручную. - person Art; 21.08.2017
comment
Дело в том, что теперь все работает, когда включены все правильные заголовочные файлы (и функции ассемблера не изменились). Я не знаю почему. Я заметил, что когда были включены правильные файлы, %rsp уменьшалось на пару байтов больше перед вызовом mmap(), возможно, была проблема с перезаписью. - person Hadron; 21.08.2017