Аргументы функции main(): argv и argc. Несколько подробностей о функции main Аргументы функции main и тип возвращаемого значения

При автоматизированном создании консольного приложения в языке программирования С++, автоматически создается главная функция очень похожая на эту:

int main(int argc, char * argv)
{…}

Заголовок функции содержит сигнатуру главной функции main() с аргументами argс и argv .
Если программу запускать через командную строку, то существует возможность передать какую-либо информацию этой программе. Для этого существуют аргументы командной строки argc и argv .
Параметр argc имеет тип int , и содержит количество параметров, передаваемых в функцию main . Причем argc всегда не меньше 1, даже когда функции main не передается никакой информации, так как первым параметром считается имя приложения.
Параметр argv представляет собой массив указателей на строки. Через командную строку можно передать только данные строкового типа.

При запуске программы через командную строку Windows можно передавать ей некоторую информацию. При этом командная строка будет иметь вид:
Диск:\путь\имя.exe аргумент1 аргумент2 …

Аргументы командной строки разделяются одним или несколькими пробелами.

Аргумент argv содержит полное имя приложения:

#include
using namespace std;

cout << argv << endl;

Return 0;
}

Результат выполнения

Пример : вычисление произведения двух целых чисел
В программе используется функция преобразования строки в целое число StrToInt() отсюда .

#include
using namespace std;
int StrToInt(char *s) {…}
int main(int argc, char * argv) {

Int a = 0, b=0;

If (argc > 1)

a = StrToInt(argv);

If (argc > 2)

b = StrToInt(argv);

cout << a <<«*» << b << «= « << a*b << endl;

Return 0;
}

Запуск программы осуществляется как

Результат выполнения

Отладка программы с аргументами командной строки

Для передачи аргументов командной строки при отладке программы необходимо обратиться к меню Свойства проекта.


На вкладке Свойства конфигурации ->Отладка выбрать Аргументы команды и задать их значения.

При запуске программы в режиме отладки введенные аргументы будут восприниматься программой как аргументы командной строки.

Бывает, что данные в программу передаются из командной строки при ее вызове. Такие данные называются аргументами командной строки. Выглядит это так, например:

./a.out test.txt ls -lt /home/peter/

Здесь вызываются программы a.out (из текущего каталога) и ls (из одного каталога, указанного в переменной окружения PATH). Первая программа из командной строки получает одно слово - test.txt, вторая - два: -lt и /home/peter/.

Если программа написана на языке C, то при ее запуске управление сразу передается в функцию main() , следовательно, именно она получает аргументы командной строки, которые присваиваются ее переменным-параметрам.

До этого мы определяли функцию main() так, как-будто она не принимает никакие параметры и ничего не возвращает. На самом деле в языке C любая функция по-умолчанию (если не определено ничего иного) возвращает целое число. В этом можно убедиться. Если записать код таким образом:

main() { printf ("Hi\n " ) ; return 0 ; }

То никакого предупреждения или ошибки при компиляции не возникнет. То же самое будет, если записать int main() . Это доказывает, что функция по-умолчанию возвращает целое число, а не ничто (void). Хотя то, что возвращает функция всегда можно "переопределить", например, voidmain() или float main() .

При вызове программы из командной строки в нее всегда передается пара данных:

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

Следует иметь в виду, что само имя программы также считается. Например, если вызов выглядит так:

./a.out 12 theme 2

То первый аргумент программы имеет значение 4, а массив строк определяется как {"./a.out", "12", "theme", "2"}.

Обратите внимание на терминологию, есть всего два аргумента программы (число и массив), но сколько угодно аргументов командной строки. Аргументы командной строки "преобразуются" в аргументы программы (в аргументы функции main()).
Эти данные (число и указатель) передаются в программу даже тогда, когда она просто вызывается по имени без передачи в нее чего-либо: ./a.out. В таком случае первый аргумент имеет значение 1, а второй указывает на массив, состоящий всего из одной строки {"./a.out"}.

То, что в программу передаются данные, вовсе не означает, что функция main() должна их принимать. Если функция main() определена без параметров, то получить доступ к аргументам командной строки невозможно. Хотя ничего вам не мешает их передавать. Ошибки не возникнет.

Чтобы получить доступ к переданным в программу данным, их необходимо присвоить переменным. Поскольку аргументы сразу передаются в main() , то ее заголовок должен выглядеть таким образом:
main (int n, char *arr)

В первой переменной (n) содержится количество слов, а во второй - указатель на массив строк. Часто второй параметр записывают в виде **arr . Однако это то же самое. Вспомним, что сам массив строк, содержит в качестве своих элементов указатели на строки. А в функцию мы передаем указатель на первый элемент массива. Получается, что передаем указатель на указатель, т.е. **arr .

Задание
Напишите такую программу:

#include int main(int argc, char ** argv) { int i; printf ("%d\n " , argc) ; for (i= 0 ; i < argc; i++ ) puts (argv[ i] ) ; }

Она выводит количество слов в командной строке при ее вызове и каждое слово с новой строки. Вызовите ее без аргументов командной строки и с аргументами.

В программе мы использовали переменные-параметры argc и argv. Принято использовать именно такие имена, но на самом деле они могут быть любыми. Лучше придерживаться этого стандарта, чтобы ваши программы были более понятны не только вам, но и другим программистам.

Практическое значение передачи данных в программу

Если у вас есть опыт работы в командной строке GNU/Linux, вы знаете, что у большинства команд есть ключи и аргументы. Например, при просмотре содержимого каталогов, копировании, перемещении в качестве аргументов указываются объекты файловой системы, над которыми выполняется команда. Особенности ее выполнения определяются с помощью ключей. Например, в команде

Cp -r ../les_1 ../les_101

cp - это имя команды, -r - ключ, а../les_1 и../les_101 - аргументы команды.

Вообще чаще всего в программы при их запуске передаются адреса файлов и "модификаторы" (это ключи) процесса выполнения программы.

Напишем программу, которая открывает указанные пользователем в командной строке файлы на запись или добавление и записывает (добавляет) туда одну и туже информацию, которую пользователь вводит с клавиатуры в процессе выполнения программы:

#include #include main (int argc, char ** argv) { int i, ch; FILE * f[ 5 ] ; if (argc < 3 || argc > 7 ) { puts ("Неверное количество параметров" ) ; return 1 ; } if (strcmp (argv[ 1 ] , "-w" ) != 0 && strcmp (argv[ 1 ] , "-a" ) != 0 ) { puts ("Первый параметр может быть либо -w, либо -a" ) ; return 2 ; } for (i= 0 ; i < argc- 2 ; i++ ) { f[ i] = fopen (argv[ i+ 2 ] , argv[ 1 ] + 1 ) ; if (f[ i] == NULL) { printf ("Файл %s нельзя открыть\n " , argv[ i+ 2 ] ) ; return 3 ; } } while ((ch = getchar () ) != EOF) for (i= 0 ; i < argc- 2 ; i++ ) putc (ch, f[ i] ) ; for (i= 0 ; i < argc- 2 ; i++ ) fclose (f[ i] ) ; return 0 ; }

Пояснения к коду:

  1. Создается массив из пяти файловых указателей. Следовательно можно одновременно открыть не более пяти файлов. Файловый указатель первого файла будет хранится в элементе массива f, второго - в f и т.д.
  2. Проверяется количество аргументов командной строки. Их должно быть не меньше трех, т.к. первый - это имя программы, второй - режим открытия файла, третий - первый или единственный файл, в который будет производится запись. Поскольку программа позволяет открыть только пять файлов, то общее число аргументов командной строки не может быть больше семи. Поэтому если количество аргументов меньше 3 или больше 7, то программа завершается, т.к. оператор return приводит к выходу из функции, даже если после него есть еще код. Возвращаемое из функции значение неравное 0, может быть интерпретировано родительским процессом, как сообщение о том, что программа завершилась с ошибкой.
  3. Проверяется корректность второго аргумента командной строки. Если он не равен ни "-w", ни "-a", то условное выражение во втором if возвращает 1 (true). Функция strcmp() позволяет сравнивать строки и возвращает 0 в случае их равенства.
  4. В цикле for открываются файлы по указанным адресам, которые начинаются с третьего элемента массива argv. Именно поэтому к i прибавляется 2, чтобы получать элементы массива argv, начиная с третьего. Выражение argc-2 указывает на количество переданных имен файлов; т.к. в argc хранится общее число аргументов командной строки, первые два из которых не являются именами файлов.
  5. Выражение argv+1 позволяет "вырезать" из строки "-w" (или "-a") подстроку "w" (или "a"), т.к. argv по сути указатель на первый элемент строки. Прибавляя к указателю единицу, мы смещаем его к следующему элементу массива.
  6. Если файл отрыть не удается, то функция fopen() возвращает NULL. В таком случае программа завершается.
  7. Каждый символ, введенный пользователем с клавиатуры, записывается во все открытые файлы.
  8. В конце файлы закрываются.

Обратите внимание, в этой версии main() никаких параметров нет. Тем не менее, многие программы нуждаются в некоторых входных данных. Например, предположим, что вы пишете программу под названием Picture, которая принимает изображение в качестве входных данных, а затем делает из этого изображения миниатюру (уменьшенная версия изображения). Как функция Picture узнает, какое изображение нужно принять и обработать? Пользователь должен сообщить программе, какой файл следует открыть. Это можно сделать следующим образом:

// Программа: Picture #include #include int main() { std::cout << "Enter name of image-file to create a thumbnail for: "; std::string filename; std::cin >> filename; // Открываем файл-изображение // Создаём миниатюру // Выводим миниатюру }

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

Рассмотрим это детальнее.

Например, вы хотите создать миниатюры для всех файлов-изображений, которые находятся в определённом каталоге. Как это сделать? Вы можете запускать эту программу столько раз, сколько есть изображений в каталоге, введя каждое имя файла вручную. Однако, если есть сотни изображений, такой подход будет, мягко говоря, не очень эффективен! Решением здесь будет написать программу, которая перебирала бы каждое имя файла в каталоге, вызывая каждый раз Picture для каждого файла.

Теперь рассмотрим случай, когда у вас есть веб-сайт, и вы хотите, чтобы он создавал миниатюру каждый раз, когда пользователь загружает изображение на сайт. Эта программа не может принимать входные данные из Интернета и следует логический вопрос: «Как тогда вводить имя файла?». Выход есть: вызов веб-сервером функции Picture автоматически каждый раз после загрузки файла.

В обоих случаях нам нужно, чтобы внешняя программа передавала имя файла в качестве входных данных в нашу программу при её запуске, вместо того, чтобы Picture сам дожидался, пока пользователь вручную введёт имя файла.

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

Передача аргументов командной строки

Исполняемые программы могут запускаться в командной строке через вызов. Например, для запуска исполняемого файла MyProgram, который находится в корневом каталоге диска C в ОС Windows вам нужно ввести:

C:\>MyProgram

Чтобы передать аргументы командной строки в MyProgram, вам нужно будет их просто перечислить после имени исполняемого файла:

C:\>MyProgram SomeContent.txt

Теперь, при запуске MyProgram, SomeContent.txt будет предоставлен в качестве аргумента командной строки. Программа может иметь несколько аргументов командной строки, разделённых пробелами:

C:\>MyProgram SomeContent.txt SomeOtherContent.txt

Это также работает и с другими операционными системами, например, с Linux (хотя структура каталогов будет отличаться от структуры каталогов в Windows).

Если вы запускаете свою программу из среды IDE, то ваша IDE должна предоставить способ ввода аргументов командной строки.

Для пользователей Visual Studio : Щёлкните правой кнопкой мыши на нужный проект в Обозревателе Решений, а затем выберите «Свойства» :

Затем «Свойства конфигурации > Отладка» . На правой панели будет строка «Аргументы команды» . Вы сможете здесь ввести аргументы командной строки, и они будут автоматически переданы вашей программе при её запуске:

Пользователям Code::Blocks нужно выбрать «Project > Set program`s arguments»:

Использование аргументов командной строки

Теперь, когда вы знаете, как передавать аргументы командной строки в программу, следующим шагом будет доступ к ним из программы. Для этого используется уже другая форма функции main(), которая принимает два аргумента (argc и argv) следующим образом:

int main(int argc, char *argv)

Также вы можете увидеть и такой вариант:

int main(int argc, char** argv)

int main (int argc , char * * argv )

Хоть оба эти варианта идентичны по своей сути, но рекомендуется использовать первый, так как он интуитивно понятнее.

argc (англ. «arg ument c ount» = «количество аргументов») — это целочисленный параметр, содержащий количество аргументов, переданных в программу. argc всегда будет как минимум 1, так как первым аргументом всегда является имя самой программы. Каждый аргумент командной строки, который предоставляет пользователь, заставит argc увеличиться на 1.

argv (англ. «arg ument v alues» = «значения аргументов») — это место, где хранятся фактические значения аргументов. Хотя объявление argv выглядит немного пугающе, но это всего лишь массив . Длина этого массива — argc .

Давайте напишем короткую программу MyArguments, которая будет выводить значения всех аргументов командной строки:

// Программа: MyArguments #include int main(int argc, char *argv) { std::cout << "There are " << argc << " arguments:\n"; // Перебираем каждый аргумент и выводим его порядковый номер и значение for (int count=0; count < argc; ++count) std::cout << count << " " << argv << "\n"; return 0; }

// Программа: MyArguments

#include

int main (int argc , char * argv )

// Перебираем каждый аргумент и выводим его порядковый номер и значение

for (int count = 0 ; count < argc ; ++ count )

std :: cout << count << " " << argv [ count ] << "\n" ;

return 0 ;

Теперь, при вызове MyArguments с аргументами командной строки SomeContent.txt и 200 , вывод будет следующим:

There are 3 arguments:
0 C:\MyArguments
1 SomeContent.txt
2 200

Параметр 0 — это путь и имя текущей программы. Параметры 1 и 2 здесь являются аргументами командной строки, которые мы передали.

Обработка числовых аргументов

Аргументы командной строки всегда передаются в качестве строк, даже если предоставленное значение является числовым. Чтобы использовать аргумент командной строки в виде числа, вам нужно будет конвертировать его из строки в число. К сожалению, в C++ это делается немного сложнее, чем оно должно быть:

#include #include #include // для std::stringstream #include // для exit() int main(int argc, char *argv) { if (argc <= 1) { // В некоторых операционных системах, argv может быть просто пустой строкой, без имени программы // Обрабатываем случай, когда argv может быть пустым или не пустым if (argv) std::cout << "Usage: " << argv << " " << "\n"; else std::cout << "Usage: " << "\n"; exit(1); } std::stringstream convert(argv); // создаём переменную stringstream с именем convert, инициализируя её значением argv int myint; if (!(convert >> myint)) // выполняем конвертацию myint = 0; // если конвертация терпит неудачу, то задаём myint значение по умолчанию std::cout << "Got integer: " << myint << "\n"; return 0; }

#include

#include

#include // для std::stringstream

#include // для exit()

int main (int argc , char * argv )

if (argc <= 1 )

// В некоторых операционных системах, argv может быть просто пустой строкой, без имени программы

// Обрабатываем случай, когда argv может быть пустым или не пустым

if (argv [ 0 ] )

std :: cout << "Usage: " << argv [ 0 ] << " " << "\n" ;

else

std :: cout << "Usage: " << "\n" ;

exit (1 ) ;

Borland С++ поддерживает три аргумента main(). Первые два - это традиционные argc и argv. Это единственные аргументы функции main(), определяемые стандартом ANSI С. Они позволяют передавать аргументы командной строки в программу. Аргументы командной строки - это информация, следующая за именем программы в командной строке операционной системы. Например, когда программа компилируется с помощью строчного компилятора Borland, набирается, как правило, bcc имя_ программы

Где имя_программы - это программа, которую необходимо откомпилировать. Имя программы передается компилятору в качестве аргумента.

Параметр argc содержит число аргументов командной строки и является целым числом. Он всегда равен, по крайней мере, 1, поскольку имя программы квалифицируется как первый аргумент. Параметр argv - это указатель на массив символьных указателей. Каждый элемент данного массива указывает на аргумент командной строки. Все аргументы командной строки - это строки. Все числа конвертируются программой во внутренний формат. Следующая программа выводит «Hello», а затем имя пользователя, если его набрать прямо за именем программы:

#include

{
if(argc!=2)
{
printf ("You forgot to type your name\n");
return 1;
}
printf("Hello %s", argv);
return 0;
}

Если назвать данную программу name, а имя пользователя Сергей, то для запуска программы следует набрать:
name Сергей.
В результате работы программы появится:
«Hello Сергей».

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

Состоит из трех строк, в то время как

Herb,Rick,Fred

Это одна строка - запятые не являются разделителями.

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

"this is a test"

Важно правильно объявить argv. Наиболее типичным методом является:

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

Ниже приведен небольшой пример по использованию аргументов командной строки. Он отсчитывает в обратном порядке от значения, указанного в командной строке, и при достижении нуля подает сигнал. Обратим внимание, что первый аргумент содержит число, преобразованное в целое число с использованием стандартной функции atoi(). Если в качестве второго аргумента присутствует строка "display", то на экране будет отображаться сам счетчик.

/* программа отсчета */

#include
#include
# include
int main(int argc, char *argv)
{
int disp, count;
if(argc<2)
{
printf("You must enter the length of the count\n");
printf ("on the command line. Try again.\n");
return 1;
}
if (argc==3 && !strcmp(argv,"display")) disp = 1;
else disp = 0;
for(count=atoi(argv); count; -count)
if (disp) printf("%d ", count);
printf("%c", "\a"); /* на большинстве компьютеров это звонок */
return 0;
}

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

Для доступа к отдельным символам командной строки следует добавить второй индекс к argv. Например, следующая программа выводит все аргументы, с которыми она вызывалась, по одному символу за раз:

#include
int main(int argc, char *argv)
{
int t, i;
for(t=0; t {
i = 0;
while(argv[t][i])
{
printf("%c", argv[t][i]);
}
printf (" ");
}
return 0;
}

Надо помнить, что первый индекс предназначен для доступа к строке, а второй - для доступа к символу строки.

Обычно argc и argv используются для получения исходных команд. Теоретически можно иметь до 32767 аргументов, но большинство операционных систем не позволяют даже близко подойти к этому. Обычно данные аргументы используются для указания имени файла или опций. Использование аргументов командной строки придает программе профессиональный вид и допускает использование программы в командных файлах.

Если подсоединить файл WILDARGS.OBJ, поставляемый с Borland С++, то можно будет использовать шаблоны в аргументах типа *.EXE. (Borland С++ автоматически обрабатывает шаблоны и соответствующим образом увеличивает argc.) Например, если подсоединить к следующей программе WILDARGS.OBJ, она выдаст, сколько файлов соответствует имени указанного в командной строке файла:

/* Скомпонуйте данную программу с WILDARGS.OBJ */

#include
int main(int argc, char *argv)
{
register int i;
printf("%d files match specified name\n", argc-1);
printf("They are: ");
for(i=1; i printf ("%s ", argv[i]);
return 0;
}

Если назвать данную программу WA, затем запустить ее как указано ниже, получим число файлов, имеющих расширение ЕХE, и список имен этих файлов:

Помимо argc и argv Borland С++ также предоставляет третий аргумент командной строки -env. Параметр env позволяет программе получить доступ к информации о среде операционной системы. Параметр env должен следовать за argc и argv и объявляется следующим образом:

Как можно видеть, env объявляется так же, как и argv. Так же, как и argv, это указатель на массив строк. Каждая строка - это строка среды, определенная операционной системой. Параметр env не имеет аналога параметра argc, который сообщал бы, сколько имеется строк среды. Вместо этого последняя строка среды нулевая. Следующая программа выводит все строки среды, определенные на текущий момент в операционной системе:

/* данная программа выводит все строки окружения */

#include
int main(int argc, char *argv, char *env)
{
int t;
for(t=0; env[t]/ t++)
printf("%s\n", env[t]);
return 0;
}

Обратим внимание, что хотя argc и argv не используются программой, они должны присутствовать в списке параметров. С не знает имена параметров. Вместо этого их использование определяется по порядку объявления параметров. Фактически можно обозвать параметр как угодно. Поскольку argc, argv и env - это традиционные имена, то лучше их использовать и далее, чтобы любой человек, читающий программу, мог мгновенно понять, что это аргументы функции main().

Для программ типичной задачей является поиск значения, определенного в строке среды. Например, содержимое строки PATH позволяет программам использовать пути поиска. Следующая программа демонстрирует, как найти строки, объявляющие стандартные пути поиска. Она использует стандартную библиотечную функцию strstr(), имеющую следующий прототип:

Char *strstr(const char *str1, const char *str2);

Функция strstr() ищет строку, на которую указывает str1 в строке, на которую указывает str2. Если такая строка найдена, то возвращается указатель на первое положение. Если не найдено соответствий, то функция возвращает NULL.

/* программа ищет среди строк окружения строку, содержащую PATH */

#include
#include
int main (int argc, char *argv, char *env)
{
int t;
for(t=0; env[t]; t++)
{
if(strstr(env[t], "PATH"))
printf("%s\n", env[t]);
}
return 0;
}

Однажды заинтересовался, содержимым стека функции main процесса в linux. Провел некоторые изыскания и теперь представляю вам результат.

Варианты описания функции main:
1. int main()
2. int main(int argc, char **argv)
3. int main(int argc, char **argv, char **env)
4. int main(int argc, char **argv, char **env, ElfW(auxv_t) auxv)
5. int main(int argc, char **argv, char **env, char **apple)

Argc - число параметров
argv - нуль-терминальный массив указателей на строки параметров командной строки
env - нуль-терминальный массив указателей на строки переменных окружения. Каждая строка в формате ИМЯ=ЗНАЧЕНИЕ
auxv - массив вспомогательных значение (доступно только для PowerPC )
apple - путь к исполняемому файлу (в MacOS и Darwin )
Вспомогательный вектор - массив с различной дополнительной информацией, такой как эффективный идентификатор пользователя, признак setuid бита, размер страницы памяти и т.п.

Размер сегмента стека можно глянуть в файле maps:
cat /proc/10918/maps

7ffffffa3000-7ffffffff000 rw-p 00000000 00:00 0

Перед тем, как загрузчик передаст управление в main, он инициализирует содержимое массивов параметров командной строки, переменных окружения, вспомогательный вектор.
После инициализации верхняя часть стека выглядит примерно так, для 64битной версии.
Старший адрес сверху.

1. 0x7ffffffff000 Верхняя точка сегмента стека. Обращение вызывает segfault
0x7ffffffff0f8 NULL void* 8 0x00"
2. filename char 1+ «/tmp/a.out»
char 1 0x00
...
env char 1 0x00
...
char 1 0x00
3. 0x7fffffffe5e0 env char 1 ..
char 1 0x00
...
argv char 1 0x00
...
char 1 0x00
4. 0x7fffffffe5be argv char 1+ «/tmp/a.out»
5. Массив случайной длины
6. данные для auxv void* 48"
AT_NULL Elf64_auxv_t 16 {0,0}
...
auxv Elf64_auxv_t 16
7. auxv Elf64_auxv_t 16 Ex.: {0x0e,0x3e8}
NULL void* 8 0x00
...
env char* 8
8. 0x7fffffffe308 env char* 8 0x7fffffffe5e0
NULL void* 8 0x00
...
argv char* 8
9. 0x7fffffffe2f8 argv char* 8 0x7fffffffe5be
10. 0x7fffffffe2f0 argc long int 8" число аргументов + 1
11. Локальные переменные и аргументы, функций вызываемых до main
12. Локальные переменные main
13. 0x7fffffffe1fc argc int 4 число аргументов + 1
0x7fffffffe1f0 argv char** 8 0x7fffffffe2f8
0x7fffffffe1e8 env char** 8 0x7fffffffe308
14. Переменные локальных функций

" - описания полей в документах не нашел, но в дампе явно видны.

Для 32 битов не проверял, но скорее всего достаточно только разделить размеры на два.

1. Обращение к адресам, выше верхней точки, вызывает Segfault.
2. Строка, содержащая путь к исполняемому файлу.
3. Массив строк с переменными окружения
4. Массив строк с параметрами командной строки
5. Массив случайной длинны. Его выделение можно отключить командами
sysctl -w kernel.randomize_va_space=0
echo 0 > /proc/sys/kernel/randomize_va_space
6. Данные для вспомогательного вектора (например строка «x86_64»)
7. Вспомогательный вектор. Подробнее ниже.
8. Нуль-терминальный массив указателей на строки переменных окружения
9. Нуль-терминальный массив указателей на строки параметров командной строки
10.Машинное слово, содержащее число параметров командной строки (один из аргументов «старших» функций см. п. 11)
11.Локальные переменные и аргументы, функций вызываемых до main(_start,__libc_start_main..)
12.Переменные, объявленные в main
13.Аргументы функции main
14.Переменные и аргументы локальных функций.

Вспомогательный вектор
Для i386 и x86_64 нельзя получить адрес первого элемента вспомогательного вектора, однако содержимое этого вектора можно получить другими способами. Один из них - обратиться к области памяти, лежащей сразу за массивом указателей на строки переменных окружения.
Это должно выглядеть примерно так:
#include #include int main(int argc, char** argv, char** env){ Elf64_auxv_t *auxv; //x86_64 // Elf32_auxv_t *auxv; //i386 while(*env++ != NULL); //ищем начало вспомогательного вектора for (auxv = (Elf64_auxv_t *)env; auxv->a_type != AT_NULL; auxv++){ printf("addr: %p type: %lx is: 0x%lx\n", auxv, auxv->a_type, auxv->a_un.a_val); } printf("\n (void*)(*argv) - (void*)auxv= %p - %p = %ld\n (void*)(argv)-(void*)(&auxv)=%p-%p = %ld\n ", (void*)(*argv), (void*)auxv, (void*)(*argv) - (void*)auxv, (void*)(argv), (void*)(&auxv), (void*)(argv) - (void*)(&auxv)); printf("\n argc copy: %d\n",*((int *)(argv - 1))); return 0; }
Структуры Elf{32,64}_auxv_t описаны в /usr/include/elf.h. Функции заполнения структур в linux-kernel/fs/binfmt_elf.c

Второй способ получить содержимое вектора:
hexdump /proc/self/auxv

Самый удобочитаемое представление получается установкой переменной окружения LD_SHOW_AUXV.

LD_SHOW_AUXV=1 ls
AT_HWCAP: bfebfbff //возможности процессора
AT_PAGESZ: 4096 //размер страницы памяти
AT_CLKTCK: 100 //частота обновления times()
AT_PHDR: 0x400040 //информация о заголовке
AT_PHENT: 56
AT_PHNUM: 9
AT_BASE: 0x7fd00b5bc000 //адрес интерпретатора, то бишь ld.so
AT_FLAGS: 0x0
AT_ENTRY: 0x402490 //точка входа в программу
AT_UID: 1000 //идентификаторы пользователя и группы
AT_EUID: 1000 //номинальные и эффективные
AT_GID: 1000
AT_EGID: 1000
AT_SECURE: 0 //поднят ли setuid флаг
AT_RANDOM: 0x7fff30bdc809 //адрес 16 случайных байт,
генерируемых при запуске
AT_SYSINFO_EHDR: 0x7fff30bff000 //указатель на страницу, используемую для
//системных вызовов
AT_EXECFN: /bin/ls
AT_PLATFORM: x86_64
Слева - название переменной, справа значение. Все возможные названия переменных и их описание можно глянуть в файле elf.h. (константы с префиксом AT_)

Возвращение из main()
После инициализации контекста процесса управление передается не в main(), а в функцию _start().
main() вызывает уже из __libc_start_main. Эта последняя функция имеет интересную особенность - ей передается указатель на функцию, которая должна быть выполнена после main(). И указатель этот передается естественно через стек.
Вообще аргументы __libc_start_main имеют вид, согласно файла glibc-2.11/sysdeps/ia64/elf/start.S
/*
* Arguments for __libc_start_main:
* out0: main
* out1: argc
* out2: argv
* out3: init
* out4: fini //функция вызываемая после main
* out5: rtld_fini
* out6: stack_end
*/
Т.е. чтобы получить адрес указателя fini нужно сместиться на два машинных слова от последней локальной переменной main.
Вот что получилось(работоспособность зависит от версии компилятора):
#include void **ret; void *leave; void foo(){ void (*boo)(void); //указатель на функцию printf("Stack rewrite!\n"); boo = (void (*)(void))leave; boo(); // fini() } int main(int argc, char *argv, char *envp) { unsigned long int mark = 0xbfbfbfbfbfbfbfbf; //метка, от которой будем работать ret = (void**)(&mark+2); // извлекаем адрес, функции, вызываемой после завершения (fini) leave = *ret; // запоминаем *ret = (void*)foo; // перетираем return 0; // вызов функции foo() }

Надеюсь, было интересно.
Удач.

Спасибо пользователю Xeor за полезную наводку.