Т.е.:
1. Ключ «интерпретировать как Си» всё-таки есть -xc
2. Этот ключ почему-то не работает в прямом шебанге
3. Мало того - в формате #!/usr/bin/tcc он ведёт себя по-разному в зависимости от места: до -run или после -run: или компилируется, но не выполняется; или выполняется, но без явно указанного типа (и поэтому определяет тип по расширению файла). 4. Но ведь -w работает же! Хоть так, хоть эдак. Кажется наврал. И кажется вообще понял что происходит.
Вопрос: А как отловить execve внутри скрипта? Хотел посмотреть, что там на самом деле ядро делает с шебангом - не могу поймать strace'ом как оно параметры передает на самом деле.
Есть, но я бы на него не сильно надеялся, т.к. семантика этого ключа говорит только о том, какой именно файл мы подсунули на вход – С, если -xc, asm – -xa или ещё что. В принципе, использовать можно, но идея не очень.
На мой взгляд, более «чистый» вариант всё таки настроить binfmt_misc.
Этот ключ почему-то не работает в прямом шебанге
Я бы сказал что он и не должен, т.к. не для того делался.
Мало того - в формате #!/usr/bin/tcc он ведёт себя по-разному в зависимости от места: до -run или после -run: или компилируется, но не выполняется; или выполняется, но без явно указанного типа (и поэтому определяет тип по расширению файла).
В принципе, он должен указываться до -run, а options как в указанном случае – tcc [options...] -run infile [arguments...].
Но вообще, если честно, то tcc это прежде всего компилятор, выполнение скриптухи это так… «приятный бонус».
Вопрос: А как отловить execve внутри скрипта? Хотел посмотреть, что там на самом деле ядро делает с шебангом - не могу поймать strace’ом как оно параметры передает на самом деле.
Just use strace -b execve ./4strace. В 4strace следующее содержимое, для простоты:
#!/usr/bin/tcc -run
#include <unistd.h>
int main(int argc, char **argv) {
execve("./t.sh", NULL, NULL); /* And this */
return(0);
}
Опция -b разве что и поддерживает только именно execve.
P.S. С -w всё просто – это отключение предупреждений. Плохая идея, да и не особо эта опция важна в нашем случае.
так и пытался делать - не попадает сам вызов tcc туда, только вызов самого скрипта, а не того, что там в шебанге написано.
Ладно.
Зашёл с другой стороны - навтыкал принтов в интересующие места в исходник tcc.
C #!env такое:
0: </usr/local/bin/tcc>
1: <-run>
2: <-w>
3: <-xc>
4: <./tcc_test.c>
TCC_OPTION_w is set
TCC_OPTION_x is set on c
Напрямую такое:
0: </usr/local/bin/tcc>
1: <-run -w -xc>
2: <./tcc_test.c>
TCC_OPTION_w is set
TCC_OPTION_x is set on c
Понятно, что argc argv разные. Но всё равно должны были бы работать - опции-то устанавливаются и так, и эдак сначала. Где-то дальше ошибка там в tcc при разборе аргументов - сбрасывает тип файла позже во втором варианте.
Да и ладно. Я там не разберусь. Какой-то очень свой вариант разбора аргументов, слишком мудрёный для меня. Достаточно, что через env работает.
так и пытался делать - не попадает сам вызов tcc туда, только вызов самого скрипта, а не того, что там в шебанге написано.
А вот этого я не совсем понял, т.к. из описания execve() следует что в int execve(const char *pathname,...) это самый pathname это может быть и бинарный файл и шелл-скрипт.
У меня в конце выхлопа strace прилетает execve("./t.sh", NULL, NULLstrace: Process 4208 detached. Т.е., в принципе, логично – я запускаю шелл-скрипт. Он и исполняется именно как скрипт.
Перекомпилил в стандартный бинарь, картина ровно та же. Чего-то не понял в томи, что нужно найти?
Pike is Extendable - with modules written in C for speed or Pike for brevity. Pike supports multiple inheritance and most other constructs you would demand from a modern programming language.
внезапно, напомнило shell на аде SparForte: githubсайт
та же идея: сначала экспериментируем с галимой скриптухой – быстро, грязно, брутально.
затем получаем возможность отконпелировать в Си, в Аду либо заменить Си модулем (исходник которого который совпадает почти буквально с такой вот скриптухой, только конпелируемый)
Аргументы, которые передаются tcc из шебанга думал подсмотреть.
Где-то в Гугле человек говорил, что это невозможно, потому что этот execve происходит внутри другого execve. Спросил у вас - мол, может знаете какой-то хитрый фокус, а человек в Гугле заблуждается.
Раз такого фокуса нет - значит подглядывал argv изнутри tcc.
-------------
Тут вот интересное Pike люди предлагают посмотреть ) Предлагаю считать, что с tcc разобрались ) Pike забавный.
Где-то в Гугле человек говорил, что это невозможно, потому что этот execve происходит внутри другого execve. Спросил у вас - мол, может знаете какой-то хитрый фокус, а человек в Гугле заблуждается.
Что я сделал. Я взял ./4strace, из неё вызвал абсолютно аналогичную программу ./test, из этой программы вызвал свою глупо хихикающую программу ./t.sh. Код весь выше.
Далее делаю strace -e execve ./4strace. Получаю выхлоп:
Т.е., первый вызов (из tcc) производится точно по такому же вызову execve() с аргументом ./4strace, ну и дальше по коду теми же вызовами и работаем. И ./test и ./t.sh благополучно вызваны, как мы видим.
Не? Не оно?
Тут вот интересное Pike люди предлагают посмотреть ) Предлагаю считать, что с tcc разобрались ) Pike забавный.
А, не, не знаю. У меня к tcc сугубо утилитарный интерес.
$ strace -e execve ./some.c
execve("./some.c", ["./some.c"], 0x7fff11aac8f0 /* 49 vars */) = 0
0: </usr/local/bin/tcc>
1: <-run -xc -w>
2: <./some.c>
TCC_OPTION_x is set on c
TCC_OPTION_w is set
Hello
The interpreter specified in the shebang line is executed by the kernel itself inside the execve call.
-----------
Кстати - тоже слегка удивительно мне: тот tcc, который сейчас утягивается из Git авторов очень сильно отличается от release с тем же номером. В том числе - они ещё и файлы *.h теперь добавили в C_TYPE. А сравнить libtcc.c из гита и из релиза по строкам просто нереально - они совсем разные. Формально версии 0.9.27 и там, и там.
Есть (странные) люди, которые go как скриптовый язык используют
Довольно удобно, я пробовал; есть шебанг для этого, JIT-компиляция, то есть компилируется только в первый запуск, а бинарники держит в кеше. То, что нужно для топикстартера, тем более что Go как-никак — Си 21-го века… Но Go всё-таки слишком вербозный для скриптового языка, Haskell оказался удобнее.
Кстати - тоже слегка удивительно мне: тот tcc, который сейчас утягивается из Git авторов очень сильно отличается от release с тем же номером.
Вот только что глянул – dev-lang/tcc-0.9.27_p20211022. Я ставил просто как emerge dev-lang/tcc и даже не парился по данному поводу. Видимо, у них идёт разработка и они не стали городить отдельную ветку почему-то.
The interpreter specified in the shebang line is executed by the kernel itself inside the execve call.
Ненене… Человек говорит почти правильно. Это справедливо для случая, если у Вас, как я Вам сразу и говорил, настроен и используется binfmt_misc. Там ниже, в том же комменте он прямо предлагает посмотреть функцию load_script:
Have a look at the load_script kernel function for more details.
В «стандартном исполнении», execve() просто заменяет код вызывающего процесса на код вызываемого процесса. Даже файловые дескрипторы может не прихлопывать при замене кода. С ядром это связано, но весьма опосредовано (ну сисколлы-то в ядре должны же исполняться, не Духом же Святым оно там реализуется!), не напрямую как при использовании binfmt_misc.
Собственно, я про необходимость настройки binfmt_misc сразу поэтому и сказал. Можно «по правильному», с полной поддержкой ядра, в kernel space (хотя, если честно, это отчасти чревато), а можно просто как простой процесс, в юзерспейсе. В нашем случае это именно что userspace и ни каких делов.
Тут можно использовать отладочные подходы, типа ptrace. Но тут мне уже реально лень залезать так глубоко, да и для себя лично смысла не вижу.
А хотелось видеть внутренности, как:
Если бы мне хотелось видеть внутренности, то я бы либо извне их получал (через ptrace), либо изнутри анализом аргументов, указанных для данного скрипта (ну там getenv() каким-нибудь, например, примерно так же, как Вы сейчас получаете переменные окружения, которые внешние для Вашего скрипта), либо настроил себе binfmt_misc и смотрел бы что там происходит, либо читал бы строку запуска из вызывающего скрипта и смотрел как будет запущен следующий скрипт или читал бы те же переменные окружения из /proc/PID/environ на лету. Ну так, чисто навскидку что на ум приходит. Чудес-то не бывает, это же просто процесс.
-b это для случая, когда не справился (или даже не пробовали использовать) связку vim, syntastic, splint. Обычно их хватает. Но если генерация была на лету, то -b в наборе опций есть, можно использовать man tcc.
56440896ea68 : at free: BCHECK: freeing invalid region
./dfree:11: by main
11я строка, да оно и есть. Придётся раскомментировать // ptr = NULL и узбагоиться. =)))
Всё как обычно, всё хорошо.
Но нет, предлагает грабельки расстелить.
Ну, то есть прикопанные грабельки в другой скриптухе, да ещё «заботливо» присыпанные листвой так, что и не докопаешься до них с первого раза, это нормально.
А тебе не приходило в голову, что для скриптухи, в которой автоуправление памятью надо из коробки, эти танцы с бубном - перебор?
Нет. У меня всё нормально. И нет проблем перевести эту скриптуху в просто код на «просто С». А не учить по 10 языков и потом переписывать с языка на язык. Тут всё нужное есть практически сразу.