C - функция Variadic компилируется, но выдает ошибку сегментации

У меня есть переменная функция в C для записи в файл журнала, но как только она вызывается, она дает ошибку сегментации в заголовке.

В основном процессе вызов имеет следующий формат:

mqbLog("LOG_INFORMATION",0,0,"Connect",0,"","Parameter received");  

и функция определяется следующим образом:

void mqbLog(char *type,
            int  numContext,
            double sec,
            char *service,
            int   sizeData,
            char *data,
            char *fmt,
            ...
            )
{  
    //write the log in the archive
}

Это компилируется хорошо. Когда я отлаживаю процесс, вызов mqbLog Функция выполнена, и она дает мне ошибку сегментации в открытой скобке функции, поэтому я могу спросить о значениях функции:

(gdb) p type
$1 = 0x40205e "LOG_INFORMATION"
(gdb) p numContext
$2 = 0
(gdb) p sec
$3 = 0
(gdb) p service
$4 = 0x0
(gdb) p sizeData
$5 = 4202649
(gdb) p data
$6 = 0x0

Любые идеи будут с благодарностью приняты.

1 ответ

Решение

Судя по выводу GDB, у вызывающей стороны не было прототипа для функции, которую он вызывал. Как заметил @JonathanLeffler, вы написали 0 вместо 0.0, так что он передает целое число, где вызываемый ожидает double,


Судя по значению указателя, это, вероятно, на Linux x86-64 с соглашением о вызовах System V, где регистр, назначенный для arg, определяется тем, что он является, например, третьим целочисленным arg. (См. Вики x86 для ABI/ документы соглашения о вызовах).

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


В этом случае звонящий ставит "Connect" (адрес) в RCX, потому что это 4-е целое число / указатель arg с этим неявным объявлением.

Звонящий ищет значение service в RDX, потому что 3-е целое число / указатель его вызывающего аргумента.

sec 0.0 в вызываемом абоненте, по-видимому, случайно. Он просто использует то, что сидело в XMM0. Или, возможно, неинициализированное пространство стека, так как вызывающая сторона установит AL=0 чтобы указать, что в регистрах не переданы аргументы FP (необходимо только для функций с переменными числами). Заметка al = количество аргументов регистра fp включает фиксированные невариационные аргументы, когда доступен прототип. Компиляция вашего звонка с доступным прототипом включает в себя mov eax, 1 перед call, См. Source+asm для компиляции с / без прототипа в проводнике компилятора Godbolt.

В другом соглашении о вызовах (например, -m32 с аргументами стека), все будет по крайней мере плохо, потому что эти аргументы будут переданы в стек, но int а также double разные размеры.


Пишу 0.0 для аргументов FP неявное объявление будет соответствовать определению. Но не делайте этого, по-прежнему ужасно вызывать необъявленные функции. использование -Wall чтобы компилятор сообщал вам, когда ваш код делает плохие вещи.

Ваша функция все еще может потерпеть крах; кто знает, какие еще ошибки есть в коде, который не показан?


Когда ваш код вылетает, вы должны посмотреть на инструкцию asm, на которой он вылетел, чтобы выяснить, какой указатель был плохим - например, запустить disas в гдб. Даже если вы сами этого не понимаете, вопрос о помощи в отладке (вместе со значениями регистра) может сильно помочь.

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