I have an Elf class which reads in Elf headers and constructs various data structures; I am using mmap and mmap the file to a variable maddr in the above class. A pointer to this class is passed to a DwarfSymTab class (which relies on a Dwarf class which does the basic read of dwarf sections) but in the constructor for this class, maddr has suddenly changed in value from when it was first initialized.
I am using dbx to watch this change, but dbx is complaining about a variable out of scope which is quite definitely defined in the class. Following are some extracts of the class definitions and the dbx output.
class Elf {
friend class DwarfSymTab;
friend class SymTab;
friend class Dwarf;
friend class HostFunc;
int fd;
ElfEhdr ehdr;
ElfEhdr *h;
uchar magic[4];
ElfSect *symtab;
ElfSect *symstr;
ElfSect *dynsym;
ElfSect *dynstr;
ElfSect *bss;
ulong dynamic;
struct stat st;
uchar *maddr;
. . .
};
class DwarfSymTab : public SymTab {
private:
Elf *elf;
Dwarf *dwarf;
DwarfRec *r;
DwarfUType *dt;
DwarfLine *dline;
MemLayout memlayout;
. . .
};
Localproc *HostFunc::coreopen(int corefd, int stabfd){
. . .
e[STAB] = new Elf();
if(e[STAB]->fdopen(stabfd) < 0){
fprintf(stderr, "Elf::open: %s\n", e[STAB]->perror());
exit(1);
}
stopped in HostFunc::coreopen at line 858 in file "hostfunc.cc"
858 e[CORE] = new Elf();
(dbx) print e[0], e[0]->maddr
e[0] = 0x81b26bc
e[0]->maddr = 0xfe6ce000 "^?ELF^A^A^A^F^A"
DwarfSymTab::DwarfSymTab(Core* c, Elf *e, SymTab *i, long reloc)
:SymTab(c, e, i, reloc) {
elf = e;
r = new DwarfRec;
}
stopped in DwarfSymTab::DwarfSymTab at line 30 in file "dwarfsymtab.cc"
30 elf = e;
(dbx) next
stopped in DwarfSymTab::DwarfSymTab at line 31 in file "dwarfsymtab.cc"
31 r = new DwarfRec;
(dbx) print e, e->maddr
e = 0x81b26bc
e->maddr = 0x69727473 "<bad address 0x69727473>"
(dbx) print elf
dbx: "elf" is not defined in the scope `pi`dwarfsymtab.cc`DwarfSymTab::DwarfSymTab(Core*,Elf*,SymTab*,long)`
(dbx) print e->fd
e->fd = 6
You can see that the pointer to the Elf class is in tact, and that maddr has been trashed in some way while for example, the fd member is fine (this is the fd of the file from which Elf data is being read ... I have omitted the dbx output showing this to be the case but it is so).
Can anyone explain what might be going on?
Postscript: I have tracked down the change in 'maddr' to a call to a function. This is how dbx shows it. I first stop in the following function, and I've arranged to pass the pointer to the Elf class as an argument:
void SymTab::read(Elf *e){
const char *error;
trace( "%d.read()", this ); VOK;
trace( "symtab modified %d", modtime() );
_root = 0;
if( error = gethdr(e) )
_warn = sf( "symbol table header: %s; go on", error );
stopped in SymTab::read at line 164 in file "symtab.cc"
164 trace( "%d.read()", this ); VOK;
(dbx) where
=>[1] SymTab::read(this = 0x81b757c, e = 0x81b25fc), line 164 in "symtab.cc"
[2] HostCore::open(this = 0x81b24a0), line 512 in "hostcore.cc"
[3] HostProcess::open(this = 0x81b1c24, ischild = 0), line 318 in "hostcore.cc"
[4] TermAction(parent = 0x818b294, obj = 0x81b1c24, pick = 0), line 160 in "term.cc"
[5] TermServe(), line 238 in "term.cc"
[6] PadsServe(n = 0), line 292 in "term.cc"
[7] main(argc = 1, av = 0xfeffdd68), line 75 in "pi.cc"
At this point, the pointer to the memory-mapped region is correct. The fd is that of the file from which elf sections are being read:
(dbx) print e, e->fd, e->maddr
e = 0x81b25fc
e->fd = 7
e->maddr = 0xfe6ce000 "^?ELF^A^A^A^F^A"
Stepping over a few instructions:
. . .
(dbx) next
stopped in SymTab::read at line 167 in file "symtab.cc"
167 if( error = gethdr(e) )
and then stepping into the routine which reads the dwarf structures:
(dbx) step
stopped in DwarfSymTab::gethdr at line 36 in file "dwarfsymtab.cc"
36 switch(elf->encoding()){
(dbx) where
=>[1] DwarfSymTab::gethdr(this = 0x81b757c, e = 0x81b25fc), line 36 in "dwarfsymtab.cc"
[2] SymTab::read(this = 0x81b757c, e = 0x81b25fc), line 167 in "symtab.cc"
[3] HostCore::open(this = 0x81b24a0), line 512 in "hostcore.cc"
[4] HostProcess::open(this = 0x81b1c24, ischild = 0), line 318 in "hostcore.cc"
[5] TermAction(parent = 0x818b294, obj = 0x81b1c24, pick = 0), line 160 in "term.cc"
[6] TermServe(), line 238 in "term.cc"
[7] PadsServe(n = 0), line 292 in "term.cc"
[8] main(argc = 1, av = 0xfeffdd68), line 75 in "pi.cc"
At this point, maddr has clearly been trampled on, but nothing else:
(dbx) print e, e->fd, e->maddr
e = 0x81b25fc
e->fd = 7
e->maddr = 0x69727473 "<bad address 0x69727473>"
Since there is no malloc'ing going on across a function call (as far as I know), what could be causing this? 'gethdr' in the DwarfSymTab class is overrinding a virtual function in the base 'SymTab' class. I don't know if this has any bearing on how the function is called.
I apologize for the amount of detail, but as I pointed out, the problem can't be really simplified (or perhaps it's beyond my abilities).
Post-postscript: In fact the situation is worse than this because of the following. It is true that by stepping into the 'gethdr' function as above, the 'maddr' member of the Elf class is incorrect, but if I go back down the stack to the calling frame, everything is fine:
(dbx) next
stopped in SymTab::read at line 167 in file "symtab.cc"
167 if( error = gethdr(e) )
(dbx) step
stopped in DwarfSymTab::gethdr at line 36 in file "dwarfsymtab.cc"
36 switch(elf->encoding()){
(dbx) print e, e->fd, e->maddr
e = 0x81b25fc
e->fd = 7
e->maddr = 0x69727473 "<bad address 0x69727473>"
(dbx) up
Current function is SymTab::read
167 if( error = gethdr(e) )
(dbx) print e, e->fd, e->maddr
e = 0x81b25fc
e->fd = 7
e->maddr = 0xfe6ce000 "^?ELF^A^A^A^F^A"
This simply doesn't make any sense to me.
Related
The function is defined like this:
1 db_bool cm_text_equal(const cm_text_t *text1, const cm_text_t *text2, db_bool case_ins)
and it will be called like this:
cm_format_element_t * cm_fetch_fmt_element(cm_text_t * fmt)
{
db_uint32 i;
cm_text_t cmp_text;
CM_POINTER(fmt);
cmp_text.str = fmt->str;
for(i = 0; i < DATE_FORMAT_COUNT; i++)
{
cmp_text.len = g_fmt_elements[i].name.len;
if(cmp_text.len > fmt->len)
{
continue;
}
//2 the problem happens here where the function be called
if(cm_text_equal(&g_fmt_elements[i].name, &cmp_text, DB_TRUE))
{
fmt->str += cmp_text.len;
fmt->len = (db_uint16)(fmt->len - cmp_text.len);
return &g_fmt_elements[i];
}
}
return NULL;
}
the g_fmt_elements[] is defined like this:
cm_format_element_t g_fmt_elements[DATE_FORMAT_COUNT] =
{
{{1, (db_char *)" "}, FMT_SPACE_SPLIT, DB_TRUE},
{{1, (db_char *)"-"}, FMT_MINUS_SPLIT, DB_TRUE},
...
}
typedef struct tagcm_format_element
{
cm_text_t name;
db_uint16 id;
db_bool reverse_used;
}cm_format_element_t;
typedef struct tagcm_text
{
db_uint32 len;
db_char * str;
}cm_text_t;
I debug the program by gdb. so before the function cm_text_equal called. the threee parameters are:
&g_fmt_elements[i].name = (cm_text_t *) 0x7f24f8766890
&cmp_text=0x7f2553ef8790
DB_TRUE=1
when cm_fetch_fmt_element is running to the line "//2 the problem...".
the i=30. and the variable g_fmt_elements is a global static variable.
you can see its definition below.
(gdb) p g_fmt_elements[30]
$57 = {name = {len = 4, str = 0x7f24f854a949 "YYYY"}, id = 226, reverse_used = 1}
(gdb) p &g_fmt_elements[30]
$58 = (cm_format_element_t *) 0x7f24f8766890
Then I step into the function cm_text_equal, and it turns out:
(gdb) s
cm_text_equal (text1=0x7f256ec655d0, text2=0x7f2553ef8790, case_ins=1) at src/db_text.c:504
so you can see, the first param was changed from 0x7f24f8766890 to 0x7f256ec655d0, and I check the regesters:
(gdb) p/x $rdi
$52 = 0x7f256ec655d0
(gdb) p/x $rsi
$53 = 0x7f2553ef8790
(gdb) p/x $rdx
$54 = 0x1
you can see the first param passed into the rdi was real changed. Does anyone know how that happend?
I also checked two different addrs.
(gdb) p *(cm_format_element_t*)0x7f256ec655d0
$55 = {name = {len = 4, str = 0x7f256ea6275c "YYYY"}, id = 226, reverse_used = 0}
(gdb) p *(cm_format_element_t*)0x7f24f8766890
$56 = {name = {len = 4, str = 0x7f24f854a949 "YYYY"}, id = 226, reverse_used = 1}
you see, 0x7f24f8766890 is the right addr and its value is the same as definition. while the other addr 0x7f256ec655d0 is wrong, but I don't understand its value almost the same as the right one. However the last member is wrong, which makes my program runs abnormally.
here is more details that I found later
thank you all for trying to help.
I guess I found where to be wrong.
It almost took me two days to figure out the reason, I am not good at. Haha.
but I just still want to answer it for others who may have the same problems.
Ok, let's begin.
Because I have restart my program, so the addr have changed. so this time
before step into the function. the add of g_fmt_elements[30] is:
(gdb) p g_fmt_elements[30]
$6 = {name = {len = 4, str = 0x2ae97faa554e "YYYY"}, id = 226, reverse_used = 1}
(gdb) p &g_fmt_elements[30]
$7 = (cm_format_element_t *) 0x2ae97fcc34d0
(gdb)
and after step into the function, the param text1 become to :
(gdb) cm_text_equal (text1=0x2ae90be468f0, text2=0x2ae926d85540, case_ins=1) at src/db_text.c:499
so, you see, the (cm_format_element_t *) 0x2ae97fcc34d0 seems to changed to text1=0x2ae90be468f0, so I also check these two addr's context:
//-->the right one
(gdb)p *(cm_format_element_t*)0x2ae97fcc34d0
{name = {len = 4, str = 0x2ae97faa554e "YYYY"}, id = 226, reverse_used = 1}
//-->the wrong one
(gdb)p *(cm_format_element_t*)0x2ae90be468f0
{name = {len = 4, str = 0x2ae90bc432e6"YYYY"}, id = 226, reverse_used = 0}
the two's context almost same but the values of "addr of str" and "reverse_used".
so, how exactly the wrong addr 0x2ae90be468f0 comes, and why the context is almost the same?
so, info the libs addr of the program and found which lib does the addr 0x2ae90be468f0 belonged to.
I have got executable module iCoreTest.exe, wich dynamicly loaded library IRTest.rs. I want to debug it via lldb c++ api.
When I create "iCoreTest.exe" process under lldb throug lldb::SBTarget::Launch(..); everything works fine. With fine, I mean I can set breakpoints BreakpointCreateByLocation and when the debugger stops on it get the event from SBListener.WaitForEvent();
Problems begins when I want to attach to the currently running process.
Create target and attach to process
m_debugData->currentTarget=m_debugData>debugger.CreateTarget(executable.c_str());
m_debugData->currentProcess = m_debugData>currentTarget.AttachToProcessWithName(m_debugData->listener, processName.c_str(), false, error);
Load module "IRTest.rs"
auto module = m_debugData->currentTarget.AddModule("IRTest.rs", "i386-pc-windows-msvc", nullptr);
After that lldb stops on "ntdll.dll`DbgBreakPoint + 1"
I execute command m_debugData->currentProcess.Continue();
So, ICoreTest.exe is running..
Add breakpoint m_debugData->currentTarget.BreakpointCreateByLocation("IRTest.st", 58);
The added breakpoint does not triggered
After this I print the existing breakpoints using the following code:
void LLDBRunner::printBreakpoints()
{
for (int i = 0; i < m_debugData->currentTarget.GetNumBreakpoints(); i++)
{
auto bp = m_debugData->currentTarget.GetBreakpointAtIndex(i);
for (int j = 0; j < bp.GetNumLocations(); j++)
{
auto loc = bp.GetLocationAtIndex(j);
lldb::SBStream stream;
loc.GetDescription(stream, lldb::DescriptionLevel::eDescriptionLevelFull);
auto str = stream.GetData();
}
}
}
And output was:
1.1: where = IRTest.rs`Add + 421 at IRTest.st:58, address = IRTest.rs[0x10001525], unresolved, hit count = 0
Which means my breakpoint is unresolved..Why? :)
Also!
When i use lldb command line breakpoint is resolved, and working:
(lldb) attach -p 17448
Process 17448 stopped
* thread #1: tid = 0x0ae0, 0x77bc8d21 ntdll.dll`DbgBreakPoint + 1, stop reason = Exception 0x80000003 encountered at address 0x77bc8d20
frame #0: 0x77bc8d21 ntdll.dll`DbgBreakPoint + 1
ntdll.dll`DbgBreakPoint:
-> 0x77bc8d21 <+1>: retl
0x77bc8d22 <+2>: int3
0x77bc8d23 <+3>: int3
0x77bc8d24 <+4>: int3
Executable module set to "iCoreTest.exe".
Architecture set to: i386-pc-windows-msvc.
(lldb) b IRTest.st:58
Breakpoint 1: where = IRTest.rs`Add + 421 at IRTest.st:58, address = 0x07ca1525
(lldb) b
Current breakpoints:
1: file = 'IRTest.st', line = 58, exact_match = 0, locations = 1, resolved = 1, hit count = 0
1.1: where = IRTest.rs`Add + 421 at IRTest.st:58, address = 0x07ca1525, resolved, hit count = 0
(lldb) c
Process 17448 resuming
Process 17448 stopped
* thread #6: tid = 0x2560, 0x07ca1525 IRTest.rs`Add(X1=2, X2=42, X3=(RANGE = 1, MIN_SCALE = -4095, MAX_SCALE = 4095)) + 421 at IRTest.st:58, stop reason = breakpoint 1.1
frame #0: 0x07ca1525 IRTest.rs`Add(X1=2, X2=42, X3=(RANGE = 1, MIN_SCALE = -4095, MAX_SCALE = 4095)) + 421 at IRTest.st:58
55 i, j : INT;
56 END_VAR
57
-> 58 tmpInteg();
59
60
61
(lldb)
UPDATE:
I write a simple program wich reproduce bug
prog.cpp:
#include <cstdio>
void doSomething(void);
void doSomething(void)
{
int loop = 0;
loop += 1;
loop += 2;
loop += 3;
}
int main(void)`
{
printf("start \n");
while(1)
{
doSomething();
}
return 0;
}
Compile it..
gcc prog.cpp -g -O0
When i`m trying to set break point
m_debugData->currentTarget.BreakpointCreateByLocation("prog.cpp", 7);
I get same result
1.1: where = a.exe`doSomething() + 6 at prog.cpp:7, address = a.exe[0x00401356], unresolved, hit count = 0
My little research:
I compare lldb behavior in two versions:
Launch new process(is ok)
Attach to process(broken)
I found that in method
lldb::break_id_t
Process::CreateBreakpointSite (const BreakpointLocationSP &owner, bool use_hardware)
line..
load_addr = owner->GetAddress().GetOpcodeLoadAddress (&GetTarget());
return LLDB_INVALID_ADDRESS in version when I attach to process.
CallStack:
liblldb.dll!lldb_private::Process::CreateBreakpointSite(const std::shared_ptr<lldb_private::BreakpointLocation> & owner, bool use_hardware) Line 2094 C++
liblldb.dll!lldb_private::BreakpointLocation::ResolveBreakpointSite() Line 523 C++
liblldb.dll!lldb_private::BreakpointLocationList::AddLocation(const lldb_private::Address & addr, bool resolve_indirect_symbols, bool * new_location) Line 254 C++
liblldb.dll!lldb_private::Breakpoint::AddLocation(const lldb_private::Address & addr, bool * new_location) Line 102 C++
liblldb.dll!lldb_private::BreakpointResolver::AddLocation(lldb_private::Address loc_addr, bool * new_location) Line 214 C++
liblldb.dll!lldb_private::BreakpointResolver::SetSCMatchesByLine(lldb_private::SearchFilter & filter, lldb_private::SymbolContextList & sc_list, bool skip_prologue, const char * log_ident) Line 184 C++
liblldb.dll!lldb_private::BreakpointResolverFileLine::SearchCallback(lldb_private::SearchFilter & filter, lldb_private::SymbolContext & context, lldb_private::Address * addr, bool containing) Line 94 C++
liblldb.dll!lldb_private::SearchFilter::DoModuleIteration(const lldb_private::SymbolContext & context, lldb_private::Searcher & searcher) Line 190 C++
liblldb.dll!lldb_private::SearchFilter::Search(lldb_private::Searcher & searcher) Line 118 C++
liblldb.dll!lldb_private::BreakpointResolver::ResolveBreakpoint(lldb_private::SearchFilter & filter) Line 62 C++
liblldb.dll!lldb_private::Breakpoint::ResolveBreakpoint() Line 355 C++
liblldb.dll!lldb_private::Target::AddBreakpoint(std::shared_ptr<lldb_private::Breakpoint> bp_sp, bool internal) Line 695 C++
liblldb.dll!lldb_private::Target::CreateBreakpoint(std::shared_ptr<lldb_private::SearchFilter> & filter_sp, std::shared_ptr<lldb_private::BreakpointResolver> & resolver_sp, bool internal, bool request_hardware, bool resolve_indirect_symbols) Line 672 C++
liblldb.dll!lldb_private::Target::CreateBreakpoint(const lldb_private::FileSpecList * containingModules, const lldb_private::FileSpec & file, unsigned int line_no, unsigned __int64 offset, lldb_private::LazyBool check_inlines, lldb_private::LazyBool skip_prologue, bool internal, bool hardware, lldb_private::LazyBool move_to_nearest_code) Line 411 C++
liblldb.dll!lldb::SBTarget::BreakpointCreateByLocation(const lldb::SBFileSpec & sb_file_spec, unsigned int line, unsigned __int64 offset) Line 832 C++
liblldb.dll!lldb::SBTarget::BreakpointCreateByLocation(const lldb::SBFileSpec & sb_file_spec, unsigned int line) Line 803 C++
liblldb.dll!lldb::SBTarget::BreakpointCreateByLocation(const char * file, unsigned int line) Line 796 C++
ConsoleApplication1.exe!Debugger::LLDBRunner::setBreakpoint(std::basic_string<char,std::char_traits<char>,std::allocator<char> > file, unsigned int line) Line 204 C++
ConsoleApplication1.exe!main() Line 28 C++
UPDATE 2:
I print 'a.exe' module sections using the following code:
for (int i = 0; i < m_debugData->currentTarget.GetNumModules(); i++)
{
auto module = m_debugData->currentTarget.GetModuleAtIndex(i);
auto moduleName = module.GetFileSpec().GetFilename();
for (int j = 0; j < module.GetNumSections(); j++)
{
auto section = module.GetSectionAtIndex(j);
auto sectionName = section.GetName();
auto addr = section.GetLoadAddress(m_debugData->currentTarget);
auto isValid = LLDB_INVALID_ADDRESS != addr;
std::cout << "Module: " << moduleName << "; Section: " << sectionName << "; IsValid: " << isValid << std::endl;
}
}
An output was:
State changed unknown->stopped
Module: a.exe; Section: .text; IsValid: 0
Module: a.exe; Section: .data; IsValid: 0
Module: a.exe; Section: .rdata; IsValid: 0
Module: a.exe; Section: .eh_frame; IsValid: 0
Module: a.exe; Section: .bss; IsValid: 0
Module: a.exe; Section: .idata; IsValid: 0
Module: a.exe; Section: .CRT; IsValid: 0
Module: a.exe; Section: .tls; IsValid: 0
Module: a.exe; Section: .debug_aranges; IsValid: 0
Module: a.exe; Section: .debug_info; IsValid: 0
Module: a.exe; Section: .debug_abbrev; IsValid: 0
Module: a.exe; Section: .debug_line; IsValid: 0
Module: a.exe; Section: .debug_frame; IsValid: 0
It's hard to say with certainty, but the python APIs and the command line apis are not entirely the same. They both have their own set of things they do internally before running the "actual" command you requested. Debugging on Windows is definitely not as mature as on other platforms, in part because there are not a lot of people using it yet. I would suggest reporting this as a bug on the lldb bug tracker.
In the meantime, maybe you can try creating a target manually, and setting the breakpoint BEFORE you attach to the process. I don't know if this will work, but resolving a breakpoint dynamically when a module is loaded, versus trying to resolve it immediately when you drop the breakpoint down are two different codepaths, so it's possible it will work if the breakpoint is already there.
I discovered that I get two different results when examining a variable, depending on whether I implicitly or explicitly use the type GDB understands that variable to be:
Navigate to my stack frame
(gdb) frame 2
#2 0x00007f6a4277e87d in PyCheckFile (fname=0x7f6a338704b8 "/usr/lib/debug/sbin", ctx=0x7f6a3803ddf8)
at python-fd.c:2054
2054 pFunc = PyDict_GetItemString(p_ctx->pDict, "check_file"); /* Borrowed reference */
Print the what p_ctx points to, as GDB understands it, implicitly using whatever GDB knows about its type.
(gdb) print *p_ctx
$26 = {backup_level = 0, python_loaded = false, plugin_options = 0x0, module_path = 0x0, module_name = 0x0,
fname = 0x0, link = 0x0, object_name = 0x0, object = 0x0, interpreter = 0x7f6a3802bb10, pModule = 0x0,
pDict = 0x0, bpContext = 0x0}
Ask GDB for the name of the type
(gdb) whatis p_ctx
type = plugin_ctx *
Specify that type name explicitly when printing p_ctx, and we get a very different output.
(gdb) print * ( (plugin_ctx *) p_ctx )
$27 = {offset = 0, pfd = 0x0, plugin_options = 0x0, fname = 0x0, reader = 0x0, writer = 0x0,
where = '\000' <repeats 16 times>, "\020\273\002\070j\177", '\000' <repeats 26 times>, "\225\031\327Mj\177\000\000\240\000\000\000\000\000\000\000%\001\000\000\000\000\000\000#e\317Mj\177\000\000\370&\322Mj\177\000\000\375\377\377\377\377\377\377\377\260\234\337Mj\177\000\000\001\000\000\000\000\000\000\000#\ntBj\177\000\000\060\000\000\000\000\000\000\000*\000\000\000\000\000\000\000\177\000\000\000\000\000\000\000#\322\000\070j\177\000\000P\aCBj\177\000\000Ȋ\260\270i\225\fbЉ\342Mj\177\000\000\000\035c\001\000\000\000\000َ\372<\375\364\343\372\300\237\342Mj\177\000\000\000\337\325"..., replace = 0}
Ask GDB to tell us about types named plugin_ctx:
(gdb) info types ^plugin_ctx$
All types matching regular expression "^plugin_ctx$":
File bpipe-fd.c:
plugin_ctx;
File python-fd.c:
plugin_ctx;
Well there's our problem; we're in python-fd.c, and when we explicitly specify a type name, we get bpipe-fd's type instead!
As evidence:
(gdb) ptype p_ctx
type = struct plugin_ctx {
int32_t backup_level;
bool python_loaded;
char *plugin_options;
char *module_path;
char *module_name;
char *fname;
char *link;
char *object_name;
char *object;
PyThreadState *interpreter;
PyObject *pModule;
PyObject *pDict;
PyObject *bpContext;
} *
compared to:
(gdb) ptype plugin_ctx
type = struct plugin_ctx {
boffset_t offset;
BPIPE *pfd;
char *plugin_options;
char *fname;
char *reader;
char *writer;
char where[512];
int replace;
}
So, when presented with multiple types named plugin_ctx, how to I tell gdb which one to use? I've tried:
(gdb) print * ( ('python-fd.c'::plugin_ctx *) p_ctx )
A syntax error in expression, near `*) p_ctx )'.
which obviously did not work. I have not found anything in GDB's manual on how to address this kind of disambiguation when it applies to types. So what's the preferred approach in this situation?
I'll flesh out this answer with examples as I get it working, but based on #n.m's feedback discussion on the core post and the information found within another thread, here's a workaround:
Create an object file with the type you want, with an unambiguous name.
#include <Python.h>
struct plugin_ctx2 {
int32_t backup_level;
bool python_loaded;
char *plugin_options;
char *module_path;
char *module_name;
char *fname;
char *link;
char *object_name;
char *object;
PyThreadState *interpreter;
PyObject *pModule;
PyObject *pDict;
PyObject *bpContext;
} ;
Using gcc's add-symbol-file command, pull that object file into the running process.
You should now be able to use the new type.
I've got a debug build of a program (the V8 JavaScript VM) and I want to understand how instances of certain classes are laid out in memory. I can pretty-print structures like this:
(gdb) print thread_local
$6 = {
blocks_ = {
data_ = 0x868ceb0,
capacity_ = 7,
length_ = 1
},
entered_contexts_ = {
data_ = 0x868d828,
capacity_ = 1,
length_ = 1
},
saved_contexts_ = {
data_ = 0x868d838,
capacity_ = 1,
length_ = 1
},
spare_ = 0x0,
ignore_out_of_memory_ = false,
call_depth_ = 1,
handle_scope_data_ = {
next = 0x0,
limit = 0x0,
level = 0
}
}
but I want to know where those various members (blocks, entered_contexts, etc.) are physically, relative to the start of the object. On Solaris-based systems, mdb can do this for C structs like so:
> ::print -at port_event_t
0 port_event_t {
0 int portev_events
4 ushort_t portev_source
6 ushort_t portev_pad
8 uintptr_t portev_object
10 void *portev_user
}
In that example, each field is prefixed with its offset from the start of the structure. I want to do the same thing for C++ classes. gdb has to have this information in order to print out the struct members, but is there any way to view it?
Alternatively, is there some other way to do this for a running program?
You can always print out the address of each member and this to figure it out yourself (you use & to get the member address, just like in the language itself).
I wish I knew.
You can use ptype to list members. Then you can fabricate a poor man's offsetof like this:
(gdb) p/a &((my_struct_*)0)->my_member
(gdb) p/a &((struct sk_buff*)0)->iif
$7 = 0x74
In 2020, modern gdb versions have ptype /o:
(gdb) ptype /o 'sead::MethodTreeNode'
/* offset | size */ type = class sead::MethodTreeNode : public sead::IDisposer, public sead::TTreeNode<sead::MethodTreeNode*>, public sead::INamable {
private:
/* 88 | 32 */ class sead::StorageFor<sead::AnyDelegate> [with T = class sead::AnyDelegate] {
private:
/* 88 | 32 */ u8 mStorage[32];
/* total size (bytes): 32 */
} mDelegateHolder;
/* 120 | 8 */ class sead::CriticalSection *mCriticalSection;
/* 128 | 4 */ u32 mPriority;
/* 132 | 4 */ sead::BitFlag32 mPauseFlag;
/* 136 | 8 */ PauseEventDelegate *mPauseEventDelegate;
/* 144 | 8 */ void *mUserID;
/* total size (bytes): 152 */
}
It'll even show you the layout of member variables that are structs/classes and the total size.
For the record, this is the original C++ code:
class MethodTreeNode : public IDisposer, public TTreeNode<MethodTreeNode*>, public INamable
{
// ...
using PauseEventDelegate = IDelegate2<MethodTreeNode*, PauseFlag>;
// ...
StorageFor<sead::AnyDelegate> mDelegateHolder;
mutable CriticalSection* mCriticalSection;
u32 mPriority;
BitFlag32 mPauseFlag;
PauseEventDelegate* mPauseEventDelegate;
void* mUserID;
};
I haven't figured out how to make it print member offsets in hexadecimal, though...
The only way I know of is x /<number of bytes>x <variable name>
This will give you a hex dump then it is up to you to read the structure.
Use pahole (not part of GDB).
Another possibility is pahole.py (not yet committed).
i have this code. the pointer turns 0x0 immediately before using it. short before, it had the correct address.
TreeViewColumn *col;
col = preview->get_column(pcFolder); /* col = 0x7fff5fc404a0 */
col->set_resizable(true); /* col = 0x0 */
i use Gtkmm 2.4, but it returns the expected value, it just turns 0x0. whats wrong?
gdb proof:
151 col = preview->get_column(pcFolder); /* col = 0x7fff5fc404a0 */
(gdb) print col
$1 = ('Gtk::TreeViewColumn' *) 0x7fff5fc404a0
(gdb) print *col
warning: can't find linker symbol for virtual table for `Gtk::TreeViewColumn' value
$2 = {
<Gtk::Object> = {
<Glib::Object> = {
<Glib::ObjectBase> = <invalid address>,
members of Glib::Object:
_vptr$Object = 0x7fff5fc06a20,
static object_class_ = {<No data fields>}
},
members of Gtk::Object:
static object_class_ = {<No data fields>},
referenced_ = 21,
gobject_disposed_ = 60
},
members of Gtk::TreeViewColumn:
static treeviewcolumn_class_ = {<No data fields>}
}
(gdb) next
152 col->set_resizable(true); /* col = 0x0 */
(gdb) print col
$3 = ('Gtk::TreeViewColumn' *) 0x0
(gdb) print *col
Cannot access memory at address 0x0
(gdb) next
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000000
0x00000001000edc68 in Gtk::TreeViewColumn::set_resizable ()
i have no idea what causes this phenomenon. do you have?
Solution:
reading the documentation. the function returning pcFolder counts from 1, get_column() from 0.
The function call:
preview->get_column(pcFolder);
returns NULL.
When gdb shows the current code line, it hasn't been executed until you type next.
You probably pass an index that is larger than the number of columns in preview. Try:
p pcFolder
p preview->get_columns().size()
preview->get_column(); returns NULL, before that, its just some random value, since you didn't initialize the col variable
Better code would actually be to initialise the variable immediately on use by calling getColumn at the point of declaration:
TreeViewColumn *col = preview->get_column(pcFolder);
If this function can return NULL (as it appears to) you must then check before you use the pointer, thus:
if( col != NULL )
{
col->set_resizable( true );
}
// else handle the "error" if you want
preview->get_column(pcFolder)
must be returning 0.