Поведение -O и -wrap

Я пытаюсь имитировать функцию, используя -wrap=symbol для модульного тестирования, и обнаруживаю различное поведение в зависимости от переданных флагов оптимизации GCC.

Рассмотрим приведенный ниже пример программы

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

void * __wrap_malloc(size_t sz)
{
   printf("Called %s", __func__);
   return (void *) 0xDEADBEEF;
}

int main()
{
    void *ptr;
    ptr = malloc(10);
    assert(0xDEADBEEF == (unsigned int)(uintptr_t)(ptr));
    return 0;
}

При использовании оптимизации

❯ gcc -Os -Wl,-wrap=malloc 1.c -o opt

❯ ./opt
opt: 1.c:16: main: Assertion `0xDEADBEEF == (unsigned int)(uintptr_t)(ptr)' failed.
[1]    12546 abort      ./opt

Без оптимизации

❯ gcc  -Wl,-wrap=malloc 1.c -o without_opt
❯ ./without_opt
Called __wrap_malloc

Я попробовал дизассемблировать исполняемый файл

С оптимизацией

   0000000000400450 <main>:
     400450:   48 83 ec 08             sub    $0x8,%rsp
     400454:   b9 20 06 40 00          mov    $0x400620,%ecx
     400459:   ba 10 00 00 00          mov    $0x10,%edx
     40045e:   be ea 05 40 00          mov    $0x4005ea,%esi
     400463:   bf ee 05 40 00          mov    $0x4005ee,%edi
     400468:   e8 d3 ff ff ff          callq  400440 <__assert_fail@plt>
     40046d:   0f 1f 00                nopl   (%rax)

Без оптимизации

   000000000040053e <main>:
     40053e:   55                      push   %rbp
     40053f:   48 89 e5                mov    %rsp,%rbp
     400542:   48 83 ec 10             sub    $0x10,%rsp
     400546:   bf 0a 00 00 00          mov    $0xa,%edi
     40054b:   e8 c7 ff ff ff          callq  400517 <__wrap_malloc>
     400550:   48 89 45 f8             mov    %rax,-0x8(%rbp)
     400554:   48 8b 45 f8             mov    -0x8(%rbp),%rax
     400558:   3d ef be ad de          cmp    $0xdeadbeef,%eax
     40055d:   74 19                   je     400578 <main+0x3a>
     40055f:   b9 4e 06 40 00          mov    $0x40064e,%ecx
     400564:   ba 10 00 00 00          mov    $0x10,%edx
     400569:   be 0a 06 40 00          mov    $0x40060a,%esi
     40056e:   bf 10 06 40 00          mov    $0x400610,%edi
     400573:   e8 c8 fe ff ff          callq  400440 <__assert_fail@plt>
     400578:   b8 00 00 00 00          mov    $0x0,%eax
     40057d:   c9                      leaveq
     40057e:   c3                      retq
     40057f:   90                      nop

Вызов Wrap_malloc отсутствует. Похоже, его оптимизировали. Затем я попытался добавить оператор печати

   int main()
   {
       void *ptr;
       ptr = malloc(10);
 >>    printf("0x%x\n", (uintptr_t)ptr);
       assert(0xDEADBEEF == (unsigned int)(uintptr_t)(ptr));
       return 0;
   }


❯ gcc -Os -Wl,-wrap=malloc 1.c -o print_opt
❯ ./print_opt
Called __wrap_malloc0xdeadbeef
print_opt: 1.c:17: main: Assertion `0xDEADBEEF == (unsigned int)(uintptr_t)(ptr)' failed.
[1]    31575 abort      ./print_opt

Теперь я вижу, как вызывается обернутая функция. Но утверждение не удалось

   0000000000400450 <main>:
     400450:   48 83 ec 08             sub    $0x8,%rsp
     400454:   bf 0a 00 00 00          mov    $0xa,%edi
     400459:   e8 f9 00 00 00          callq  400557 <__wrap_malloc>
     40045e:   bf 0a 06 40 00          mov    $0x40060a,%edi
     400463:   48 89 c6                mov    %rax,%rsi
     400466:   31 c0                   xor    %eax,%eax
     400468:   e8 c3 ff ff ff          callq  400430 <printf@plt>
     40046d:   b9 48 06 40 00          mov    $0x400648,%ecx
     400472:   ba 11 00 00 00          mov    $0x11,%edx
     400477:   be 10 06 40 00          mov    $0x400610,%esi
     40047c:   bf 14 06 40 00          mov    $0x400614,%edi
     400481:   e8 ba ff ff ff          callq  400440 <__assert_fail@plt>
     400486:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
     40048d:   00 00 00

Может ли кто-нибудь помочь мне понять, почему/как я вижу такое поведение? заранее спасибо

Оптимизатор знает, что malloc() никогда не сможет вернуть это значение, поэтому он просто компилирует его как неудавшееся утверждение.

Barmar 10.05.2024 22:49

Вы пробовали заменить DEADBEEF на DEADC0D0? что-то выровненное по 4, 8 или 16 байт, чтобы сделать его надежным возвращаемым значением для malloc, которое, вероятно, не может возвращать нечетные адреса

Jean-François Fabre 10.05.2024 23:37

@ Жан-Франсуа Фабр Вы правы. Он работает после изменения проверки возврата и подтверждения на 0xDEADC0D0.

suren99 10.05.2024 23:50
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
3
94
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В gcc malloc() рассматривается как так называемая «встроенная» функция, о которой оптимизатор знает и которую обрабатывает особым образом (например, он может делать определенные предположения относительно возвращаемого значения, что приводит к автоматическому сбою утверждения без нужно проверить его во время выполнения или даже вызвать malloc()). Если вы добавите -fno-builtin-malloc к параметрам компиляции, эта специальная обработка будет отключена:

$ gcc -Os -fno-builtin-malloc -Wl,-wrap=malloc 1.c -o opt
$ ./opt
Called __wrap_malloc

Примечание: я добавил завершающую новую строку к вашему выводу printf(), потому что программа, которая создает текстовый вывод без нее, может вызвать странные эффекты в вызывающей оболочке - например, без нее строка была перезаписана приглашением моей оболочки. Или вы можете получить конечный % или другое поведение.

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