Вопрос по поводу регистров
От: YourLastSong  
Дата: 09.09.11 20:10
Оценка:
Здравствуйте, уважаемые господа.

У меня появилось неск. глупых вопросов по поводу регистров процессора.

— Через какой регистр в x64 передаётся значение, возвращаемое из какой-либо функции?

— В какой регистр в x64 записывается адрес след. для выполнения строки кода? Возможно, не совсем прав. сказал — имею ввиду код, который заносится в стек перед вызовом функции через call.

— Пожалуй, самый глупый вопрос. Как вообще устроены регистры в x64? Они полностью заменили их x86 аналоги или нет? Регистры x86 всё равно остались ведь или как?

Заранее благодарю за возможные ответы.
Re: Вопрос по поводу регистров
От: мыщъх США http://nezumi-lab.org
Дата: 09.09.11 20:44
Оценка:
Здравствуйте, YourLastSong, Вы писали:

YLS>Здравствуйте, уважаемые господа.


YLS>У меня появилось неск. глупых вопросов по поводу регистров процессора.


YLS>- Через какой регистр в x64 передаётся значение, возвращаемое из какой-либо функции?

курите соглашения о передаче/возврате параметов, вот тут кое-что есть
http://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions
но это сильно зависит от компилятора (если только функция не на экспорт) и типа значения.

YLS>- В какой регистр в x64 записывается адрес след. для выполнения строки кода?

RIP вестимо

YLS> Возможно, не совсем прав. сказал — имею ввиду код,

YLS> который заносится в стек перед вызовом функции через call.
в стек заносится не код, а адрес возврата. стеком заведует RSP

YLS> Пожалуй, самый глупый вопрос. Как вообще устроены регистры в x64?

YLS> Они полностью заменили их x86 аналоги или нет? Регистры x86 всё равно остались ведь или как?
регистров стало больше. это раз. регистры стали длиннее это два. в силу того, что регистров стало больше, то в винде и многих других осях появилась возможность передавать аргументы через регистры, а не через стек (хотя без стека тоже не обошлось).

но какое это отношение имеет к си и плюсам? никакого контроля над регистрами у вас нет.
americans fought a war for a freedom. another one to end slavery. so, what do some of them choose to do with their freedom? become slaves.
Re[2]: Вопрос по поводу регистров
От: Tilir Россия http://tilir.livejournal.com
Дата: 09.09.11 21:38
Оценка:
Здравствуйте, мыщъх, Вы писали:

YLS>>- Через какой регистр в x64 передаётся значение, возвращаемое из какой-либо функции?

М>курите соглашения о передаче/возврате параметов, вот тут кое-что есть
М>http://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions
М>но это сильно зависит от компилятора (если только функция не на экспорт) и типа значения.

Это зависит от ABI, а не от компилятора.
Re[3]: Вопрос по поводу регистров
От: мыщъх США http://nezumi-lab.org
Дата: 09.09.11 21:59
Оценка:
Здравствуйте, Tilir, Вы писали:

T>Здравствуйте, мыщъх, Вы писали:


YLS>>>- Через какой регистр в x64 передаётся значение, возвращаемое из какой-либо функции?

М>>курите соглашения о передаче/возврате параметов, вот тут кое-что есть
М>>http://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions
М>>но это сильно зависит от компилятора (если только функция не на экспорт) и типа значения.
T>Это зависит от ABI, а не от компилятора.
см. выделенное. если соглашение о передаче параметров задано явно, то компилятор ему подчиняется. если же соглашение не задано -- на усмотреение компилятора. кстати, у всех оно очень сильно разное. достаточно посмотреть в ассемблерный листинг.
americans fought a war for a freedom. another one to end slavery. so, what do some of them choose to do with their freedom? become slaves.
Re[4]: Вопрос по поводу регистров
От: Tilir Россия http://tilir.livejournal.com
Дата: 09.09.11 22:15
Оценка:
Здравствуйте, мыщъх, Вы писали:

M>см. выделенное. если соглашение о передаче параметров задано явно, то компилятор ему подчиняется. если же соглашение не задано -- на усмотреение компилятора. кстати, у всех оно очень сильно разное. достаточно посмотреть в ассемблерный листинг.


Можно пример двух компиляторов с разной конвенцией вызова для static функций? Желательно Linux/x86. Возможно вы знаете больше меня в этом вопросе
Re[4]: Вопрос по поводу регистров
От: ononim  
Дата: 09.09.11 22:27
Оценка: 8 (2)
YLS>>>>- Через какой регистр в x64 передаётся значение, возвращаемое из какой-либо функции?
М>>>курите соглашения о передаче/возврате параметов, вот тут кое-что есть
М>>>http://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions
М>>>но это сильно зависит от компилятора (если только функция не на экспорт) и типа значения.
T>>Это зависит от ABI, а не от компилятора.
М>см. выделенное. если соглашение о передаче параметров задано явно, то компилятор ему подчиняется. если же соглашение не задано -- на усмотреение компилятора. кстати, у всех оно очень сильно разное. достаточно посмотреть в ассемблерный листинг.
На самом деле задано не задано — пофиг. Если вызов не "торчит наружу" собираемого модуля (например как экспорт, как параметр внешнего api, или как аргумент в asm коде) — компилятор может пихать параметры куда и как ему заблагорассудится. Более того — я видел и такие случаи:
Случай 1: есть проект-длл, у длл есть экспортируемая функция. Торчит как __stdcall экспорт все дела, если дернуть этот экспорт аргументы принимаются и обрабатываются как полагается. Эта же функция юзается местами из самой длл. Так вот — ее вызов внутри самой длл иногда делался не в самое начало этой функции (куда указывает экспорт) а чутка после пролога. Разумеется вызывающий код при этом пихал все параметры в регистры, в те самые куда укладывал аргументы __stdcall пролог из стека.
Случай 2: написали мы хук одной системной API. Какой — не помню. Помню тока что она торчала как __stdcall экспорт не то из Ole32 не то из shell32. Вобщем после портирования на win7/х64 стал код ее юзающий внутри этой самой длл крэшится после того как мы ее хукаем. Начали копать -выяснилось, что код который крэшится, полагается на то, что эта функция не меняла какой-то general purpose регистр, который при __stdcall менять _разрешается_. То есть функция была как нормальный __stdcall, но компилятор, компиляя эту длл знал, что
конкретный регистр который при __stdcall сохранять не обязательно, конкретно эта функция не меняет, и полагался на это, генеря ее вызов в пределах той самой длл. А наш хук этот регистр менял, формально оставаясь __stdcall — совместимым.
Так что мораль сих басней такова — оптимизаторы нынче ваще звери бессердечные
Как много веселых ребят, и все делают велосипед...
Re[5]: Вопрос по поводу регистров
От: мыщъх США http://nezumi-lab.org
Дата: 09.09.11 22:59
Оценка: 6 (1)
Здравствуйте, ononim, Вы писали:

YLS>>>>>- Через какой регистр в x64 передаётся значение, возвращаемое из какой-либо функции?

М>>>>курите соглашения о передаче/возврате параметов, вот тут кое-что есть
М>>>>http://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions
М>>>>но это сильно зависит от компилятора (если только функция не на экспорт) и типа значения.
T>>>Это зависит от ABI, а не от компилятора.
М>>см. выделенное. если соглашение о передаче параметров задано явно, то компилятор ему подчиняется. если же соглашение не задано -- на усмотреение компилятора. кстати, у всех оно очень сильно разное. достаточно посмотреть в ассемблерный листинг.
O>На самом деле задано не задано — пофиг. Если вызов не "торчит наружу" собираемого модуля (например как экспорт, как параметр внешнего api, или как аргумент в asm коде) — компилятор может пихать параметры куда и как ему заблагорассудится.

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

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

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

O>Случай 1: есть проект-длл, у длл есть экспортируемая функция. Торчит как __stdcall экспорт все дела, если дернуть этот экспорт аргументы принимаются и обрабатываются как полагается. Эта же функция юзается местами из самой длл. Так вот — ее вызов внутри самой длл иногда делался не в самое начало этой функции (куда указывает экспорт) а чутка после пролога. Разумеется вызывающий код при этом пихал все параметры в регистры, в те самые куда укладывал аргументы __stdcall пролог из стека.


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

O> в пределах той самой длл. А наш хук этот регистр менял, формально оставаясь __stdcall

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

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

O>Так что мораль сих басней такова — оптимизаторы нынче ваще звери бессердечные

оптимизаторы под питон радуют еще больше. во-первых, они не все поддерживают. во вторых, обнаружилось, что pypy.py имеет баг в реализации popen3, ведущий к течи дескрипторов. и мой скрипт за выходные дни утащил больше 2х млн. дескрипторов. и как ни странно, хрюша его выдержала, хотя работала очень меееедленно. я отписал разработчикам, а они мне сказали -- глаза разуй, сказано же -- фукнция устаревшая. ССЗБ.

питон меня уже достал. это песня. написать программу, чтобы она работала на 2.4, 2.6 и 2.7 версиях (не говоря за версию 3) и так же работала под jit и front-end c компиляторами -- надо очень сильно извратиться. на си хоть стандарт есть, а тут... только эксперимент.

с другой стороны -- оптмизаторы (как для питона, так и для си) обнаруживают много спящих багов в программе, которые годами работали бы под отладочной версией...
americans fought a war for a freedom. another one to end slavery. so, what do some of them choose to do with their freedom? become slaves.
Re[6]: Вопрос по поводу регистров
От: ononim  
Дата: 10.09.11 08:34
Оценка:
М>но это фигня. однажды я очень сильно удивлся, когда отладочный вариант программы работал, а оптимизированный -- отказывался логгировать события. только потому что функция логгирования была названа log, а компилятор (ms vc) понял это название буквально. но не как логгер, а как логарифм и предложил свою реализацию, которая была быстрее моей, но вот беда -- ни хрена не логгировала.
дык это интринсик. Скорее фича компилятора, а не просто оптимизация: http://msdn.microsoft.com/en-us/library/5704bbxw(v=vs.80).aspx
Как много веселых ребят, и все делают велосипед...
Re[6]: Вопрос по поводу регистров
От: watch-maker  
Дата: 10.09.11 09:05
Оценка:
Здравствуйте, мыщъх, Вы писали:

М>так а я о чем? (см. выделенное). ес-но, задавать конверсию имеет смысл только если функция вызывается из другого модуля, возможно, собранного другим компилятором или это каллбэк какой. и вот с каллбэками уже ньюансы. компилятору трудно понять каллбэк это или не каллбэк. и потому игнорировать явно заданную конверсию он может только в агрессивных режимах оптимизации, да и то после того, как решит, что функция никому не передается как указатель. учитывая, что глобальная оптимизация поддерживается далеко не всеми компиляторами, считаем, что среднестатистический компилятор на момент трансляции foo имеет немного шансов узнать передается ли указатель на функцию foo функции bar.


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

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





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


М>но это фигня. однажды я очень сильно удивлся, когда отладочный вариант программы работал, а оптимизированный -- отказывался логгировать события. только потому что функция логгирования была названа log, а компилятор (ms vc) понял это название буквально. но не как логгер, а как логарифм и предложил свою реализацию, которая была быстрее моей, но вот беда -- ни хрена не логгировала.


А они и не должны проверять настоящий это memcpy или пользовательская реализация. Подменил memcpy или log, получай Undefined Behaviour, как в стандарте написано:
С99>7.1.3.2 If the program declares or defines an identifier in a context in which it is reserved (other than as allowed by 7.1.4), or defines a reserved identifier as a macro name, the behavior is undefined.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.