背景
我有一个很大的模拟工具,我需要了解它的逻辑行为。为了做到这一点,如果我有一个函数调用的时间顺序,我将得到的大部分帮助,用于最小的工作示例。
我在网上找到了很多工具,例如CygProfiler和etrace。我找到了一个解决方案,我开始遵循使用调试器“步入”的最疯狂的解决方案,我变得非常痛苦。如果你有一个小程序但不是一个完整的模拟工具,这是一个很好的选择。
问题:
我面临的一个问题是上述解决方案最初用于C
,并且在编译时会生成静态文件(*.o
)。另一方面,模拟工具生成共享库(.so
)。我对较低级别的东西知之甚少,所以当我尝试链接它们时,我似乎失败了。
我专门查看了etrace
documentation,并说:
要了解如何修改ptrace.c以使用动态库,请查看 example2目录。那里的消息来源也创造了一个独立的 可执行文件,但PTRACE_REFERENCE_FUNCTION宏定义如下 它将用于动态库。
如果您查看回购,example
和example2
文件夹中的文件没有区别。只有.h
中有一个额外的example2
文件。
另一方面,如果你看src/ptrace.c
那就说:
在动态库上使用ptrace时,必须设置 PTRACE_REFERENCE_FUNCTION宏是一个函数的名称 图书馆。加载时此函数的地址将是第一个 行输出到跟踪文件并允许转换 其符号名称的其他入口和出口指针。你可以设置 宏PTRACE_INCLUDE,需要任何#include指令 该函数可以访问此源文件。
下面有评论代码:
/* When using ptrace on a dynamic library, the following must be defined:
#include "any files needed for PTRACE_REFERENCE_FUNCTION"
#define PTRACE_REFERENCE_FUNCTION functionName
`*/
问题:
本质上问题如下:如何将etrace
与动态库结合使用?
我是否需要#include任何文件?
要跟踪独立程序,不需要#include any 附加文件。只需将您的代码链接到ptrace.c并使用 -finstrument-functions选项作为gcc的编译选项。这应该做到。
如何将通过makefile构建的C ++代码链接到ptrace.c
最后的注意事项:如果有人因我的无知而承担责任并为我的问题提供逐步解决方案,我将不胜感激。
更新1:
我设法将与etrace相关的库添加到模拟工具中,并且执行正常。
但是,(可能是因为脚本太旧,或者不适合与C ++一起使用)使用etrace
Hexadecimal number > 0xffffffff non-portable"
这可能会改变这个问题的性质,在这一点上更多地转向与perl相关的问题。
如果这个问题得到解决,我希望etrace
可以处理一个复杂的项目,我会提供详细信息
更新2:
我接受了@Harry的建议,我相信这对大多数项目都有效。但是在我的情况下,我从perl脚本中获得以下内容:
Use of uninitialized value within %SYMBOLTABLE in list assignment at etrace2.pl line 99, <CALL_DATA> line 1.
\-- ???
| \-- ???
\-- ???
| \-- ???
| | \-- ???
\-- ???
| \-- ???
\-- ???
| \-- ???
\-- ???
| \-- ???
\-- ???
| \-- ???
\-- ???
| \-- ???
由于autegenerated makefile我使用LD_PRELOAD加载etrace.so的共享库,我得到如下:
gcc -g -finstrument-functions -shared -fPIC ptrace.c -o etrace.so -I <path-to-etrace>
我在工具中创建了虚拟etrace.h:
#ifndef __ETRACE_H_
#define __ETRACE_H_
#include <stdio.h>
void Crumble_buy(char * what, int quantity, char * unit);
void Crumble_buy(char * what, int quantity, char * unit)
{
printf("buy %d %s of %s\n", quantity, unit, what);
}
#endif
并使用Crumble_buy
用于#define
和etrace.h
用于#include
。
答案 0 :(得分:4)
十六进制数&gt; 0xffffffff非便携式&#34;
这是来自hex
的警告,因为它检测到可能的非便携值(某些> 32位)。
在脚本的最顶部,添加:
use bigint qw/hex oct/;
当编写这个工具时,我怀疑人们在32位机器上。您可以使用带有标记-m32
的32位编译程序,但如果您更改了上面提到的perl脚本,则您不需要。
请注意,如果您使用的是Mac,则无法使用mknod
在脚本中使用的方式来创建管道;你需要使用没有参数的mkfifo
。
在Linux上,添加上面的bigint
修复程序有效。然后,您需要从同一目录运行这两个命令,我使用example2
:
../src/etrace.pl crumble
# Switch to a different terminal
./crumble
我在Mac和Linux上得到了这个
\-- main
| \-- Crumble_make_apple_crumble
| | \-- Crumble_buy_stuff
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | \-- Crumble_prepare_apples
| | | \-- Crumble_skin_and_dice
| | \-- Crumble_mix
| | \-- Crumble_finalize
| | | \-- Crumble_put
| | | \-- Crumble_put
| | \-- Crumble_cook
| | | \-- Crumble_put
| | | \-- Crumble_bake
加载动态库时,目标文件中的地址不是运行时将使用的地址。 etrace所做的是从您指定的标题中获取函数名称。例如,在example2
的情况下,这将是以下内容:
#include "crumble.h"
#define PTRACE_REFERENCE_FUNCTION Crumble_buy
然后,您将编辑makefile以确保可以找到头文件:
CFLAGS = -g -finstrument-functions -I.
请注意添加包含-I.
。标题中的符号地址(在我们的例子中,Crumble_buy
)用于计算目标文件和实际地址之间的偏移量;这允许程序计算正确的地址以找到符号。
如果你查看nm
的输出,你会得到如下内容:
0000000100000960 T _Crumble_bake
00000001000005b0 T _Crumble_buy
0000000100000640 T _Crumble_buy_stuff
00000001000009f0 T _Crumble_cook
左边的地址是相对的,也就是说,在运行时,这些地址实际上是变化的。 etrace.pl程序将这些存储在这样的哈希中:
$VAR1 = {
'4294969696' => '_Crumble_bake',
'4294969424' => '_Crumble_put',
'4294970096' => '_main',
'4294969264' => '_Crumble_mix',
'4294970704' => '_gnu_ptrace_close',
'4294967296' => '__mh_execute_header',
'4294968752' => '_Crumble_buy',
'4294968896' => '_Crumble_buy_stuff',
'4294969952' => '_Crumble_make_apple_crumble',
'4294969184' => '_Crumble_prepare_apples',
'4294971512' => '___GNU_PTRACE_FILE__',
'4294971504' => '_gnu_ptrace.first',
'4294970208' => '_gnu_ptrace',
'4294970656' => '___cyg_profile_func_exit',
'4294970608' => '___cyg_profile_func_enter',
'4294969552' => '_Crumble_finalize',
'4294971508' => '_gnu_ptrace.active',
'4294969840' => '_Crumble_cook',
'4294969088' => '_Crumble_skin_and_dice',
'4294970352' => '_gnu_ptrace_init'
};
请注意前导下划线,因为这是在使用clang的Mac上。在运行时,这些地址不正确,但它们的相对偏移量是。如果可以计算偏移量,可以调整运行时获得的地址以查找实际符号。执行此操作的代码如下:
if ($offsetLine =~ m/^$REFERENCE_OFFSET\s+($SYMBOL_NAME)\s+($HEX_NUMBER)$/) {
# This is a dynamic library; need to calculate the load offset
my $offsetSymbol = "_$1";
my $offsetAddress = hex $2;
my %offsetTable = reverse %SYMBOLTABLE;
print Dumper(\%offsetTable);
$baseAddress = $offsetTable{$offsetSymbol} - $offsetAddress;
#print("offsetSymbol == $offsetSymbol\n");
#print("offsetAddress == $offsetAddress\n");
#print("baseoffsetAddress == $offsetAddress\n");
$offsetLine = <CALL_DATA>;
} else {
# This is static
$baseAddress = 0;
}
这就是#define PTRACE_REFERENCE_FUNCTION Crumble_buy
行的含义。 ptrace中的C
代码正在使用该MACRO,如果已定义,则输出该函数的地址作为第一件事。然后计算偏移量,对于所有后续地址,按此数量调整它们,在散列中查找正确的符号。