2015-11-22 2 views
2

Когда я objdump мой a.out, я обнаружил, что были использованы __IO_putc.Как предотвратить генерацию __IO_putc?

Я знаю, что gcc будет использовать putc для замены printf, когда ввод printf прост. Но зачем gcc заменить мой putc на __IO_putc? Могу ли я предотвратить эту замену, используя некоторую командную строку, например -U_FORTYFY (которая отключает __printf_chk) или --fno-stack-protector (что отключает __stack_chk_fail)?

+3

Любая причина, по которой вы не хотите, чтобы эти символы были в двоичном формате? – Macmade

+0

__IO_putc - внутренняя функция glibc, но эта функция создает проблемы, когда я хочу сделать что-то вроде кросс-компиляции или двоичного перевода. – hwliu

+0

Вы спрашиваете, как предотвратить замену 'printf' на' putc'? –

ответ

4

почему НКУ заменить мой putc в __IO_putc

В вашем /usr/include/stdio.h, вы найдете эту строку:

#define putc(_ch, _fp) _IO_putc (_ch, _fp) 

Могу ли я предотвратить эту замену, используя некоторую командную строку

Вы можете поместить #undef putc в свой источник после #include <stdio.h>, или заключите имя функции в круглых скобках в каждом месте вызова, например, так:

(putc)('a', stdout); 

В общем, вы должны не бардак со стандартными функциями, как этот.

+0

Спасибо, это работа :) – hwliu

+0

Собственно, '#undef putc' совершенно легален (см. Раздел 7.1.4 стандарта C). * Переопределить * это не будет. Я предпочитаю использовать круглые скобки, чтобы избежать расширения, потому что он не имеет глобального эффекта, но в этом случае это, вероятно, не имеет значения, и оба варианта явно разрешены стандартом. – rici

+0

@rici Спасибо. Я обновил ответ. –

2

Вы всегда можете избежать расширения функциональных подобных макросов с помощью скобки имя:

(putc)('c', outfile); 

Это специально разрешается стандартом C, в § 7.1.4 («Использование библиотечных функций») (акцент добавлено):

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

В частном случае стандартной реализации библиотеки в комплекте с НКОЙ, вы, вероятно, обнаружите, что использование fputc вместо putc результатов фактического вызова fputc. (Стандарт требует базовой функциональности fputc, чтобы быть идентичной putc, это позволяет макро реализации putc - если есть один - оценить второй аргумент более чем один раз.)


Для чего это стоит, я проверил использование gcc 5.2.0 и оказалось, что fprintf переведен на звонок fputc, а не putc. Явное обращение putc действительно расширило макрос до вызова __IO_putc, но этого можно избежать, как указано выше.

Пример программы:

#include <stdio.h> 
int main() { 
    FILE* outfile = stdout; 
    fprintf(outfile, "c"); 
    putc('c', outfile); 
    (putc)('c', outfile); 
    return 0; 
} 

Сгенерированный код (добавлены комментарии):

main: 
    pushq %rbx 
    movq stdout(%rip), %rbx 
    movl $99, %edi 
    movq %rbx, %rsi 
    call fputc   # fprintf(outfile, "c") 
    movq %rbx, %rsi 
    movl $99, %edi 
    call _IO_putc  # putc('c', outfile) 
    movq %rbx, %rsi 
    movl $99, %edi 
    call putc   # (putc)('c', outfile); 
    xorl %eax, %eax 
    popq %rbx 
    ret 
2

putc переводится в __IO_putc на этапе предварительной обработки, в связи с расширением макросов из <stdio.h>. Вы действительно можете попытаться предотвратить это, используя fputc или взломать файл заголовка или #undef ining putc после #include <stdio.h>, но это неправильное исправление, вам просто повезло, если он работает, и больше проблем возникнет позже, так как вы полагаетесь на больше возможностей библиотеки.

gcc сконфигурирован для компиляции для данной цели с комбинацией параметров конфигурации и набора файлов заголовков и библиотек. Важно понимать, что заголовочные файлы встроены в соответствующие библиотеки для каждой конкретной версии. Вы не можете просто скомпилировать файл заголовка <stdio.h> и ожидать ссылки на библиотеку C для другой цели. Для кросс-компиляции необходима правильная настройка всей цепочки инструментов. Если у вас есть определенные версии библиотеки C, на которые вы хотите установить ссылку, используйте файлы заголовков, которые идут с ней.

OTOH, если вы просто хотите, чтобы исходные файлы использовали вашу версию стандартных функций ввода-вывода, сначала предотвращение расширения и связывания с вашей библиотекой могло бы выполнить эту работу. Альтернативой является переопределение putc и все другие функции, которые вы хотите заменить, заменяя все вызовы на вызовы на ваши замены, но вы все еще возитесь со стандартными идентификаторами библиотек, поэтому все может случиться.

+0

Переопределение стандартного имени библиотеки - неопределенное поведение. § 7.1.3. Просто говорю'. – rici

+0

@rici: Интересно! на самом деле, согласно C11 7.1.3, как '# undef'ining, так и' # define'ing 'putc' считаются вызывающими неопределенное поведение. Я отредактировал свой ответ, чтобы не поощрять такие издержки. – chqrlie

+0

Как я читал, в 7.1.3 говорится, что '# undef' имени библиотеки выдает неопределенное поведение, если это специально не разрешено в 7.1.4, что в этом случае и есть. – rici

0

Несколько иной подход у меня:

При замене стандартных функций библиотеки с вашей собственной версией библиотеки. вы, вероятно, захотите написать свою собственную версию «stdio.h», а не использовать ту, которая поставляется вместе с компилятором. Или, если вы используете тот, который предоставляет компилятор, вы, вероятно, захотите реализовать низкоуровневую IO-функцию, которую он фактически использует, вместо замены функций более высокого уровня (другими словами, выяснить, что делает __IO_putc, и замените все, что есть, и т. д.).

Чтобы «испортить» содержимое стандартных заголовков, сделав #undef или аналогичный в вашем коде, скорее всего, введет новые проблемы в другое место и, скорее всего, вызовет проблемы, если вы когда-либо захотите импортировать большой проект чужого кода. Если вместо этого у вас есть собственная версия «stdio.h», которая просто не делает #define putc ... в первую очередь, тогда у вас нет проблем, которые необходимо решить позже - и до тех пор, пока вы делаете все внешнее продуктов, он будет работать.

Важно понимать, что «stdio.h» и его друзья соединены с определенной библиотекой времени выполнения C (в большинстве случаев с некоторыми компиляторами), поэтому, если вы заменяете библиотеку времени выполнения C, вы также должны заменить соответствующий заголовочный файл тем, который работает для библиотеки (и вашего компилятора по вашему выбору)

+0

Технически вам необходимо заменить ** все ** библиотечные функции, которые используют структуру 'FILE', или, по крайней мере, перекомпилировать те, которые вы не замените своей новой версией. Оба требуют передового владения языком и его библиотеками. – chqrlie

+0

Да, и если вы фактически не поддерживаете их всех, включая заголовочный файл с прототипами для них, это просто глупо, поскольку они не будут работать, если ни одна из них не будет на 100% совместима (в этом случае ничего не нужно делать , и этого вопроса не будет) или они будут реализованы. Отсюда мое предложение о наличии собственного заголовочного файла. Это, в частности, если вы поддерживаете только подмножество! –

+0

Боюсь, этого недостаточно, потому что библиотека стандартной жизни, такая как glibc, содержит множество функций, которые используют stdio.h за пределами стандартных. Поэтому переопределение их всех безнадежно и переопределение только подмножества, которое будет связано вместо собственной библиотеки, может создавать несоответствия с ужасающими последствиями. '# define'ing подмножества для обозначения оберток, определенных пользователем, на самом деле менее рискован. – chqrlie

Смежные вопросы