Skip to content
This repository has been archived by the owner on Jan 13, 2022. It is now read-only.

Dealing with Unity dsyms and integration tests #26

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.dep
*.o
config.mk.local
test/fixtures/*
atosl
Guardfile
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ dist: clean
gzip ${DIST}.tar
rm -rf ${DIST}

test: all
rake

install: all
mkdir -p ${DESTDIR}${PREFIX}/bin
cp -f atosl ${DESTDIR}${PREFIX}/bin
Expand Down
7 changes: 7 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require 'rake/testtask'

task :default => [:test]

Rake::TestTask.new do |t|
t.pattern = "test/*_test.rb"
end
146 changes: 95 additions & 51 deletions atosl.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,13 @@ char *demangle(const char *sym)
return demangled;
}

static int compare_symbols(const void *a, const void *b)
{
struct symbol_t *sym_a = (struct symbol_t *)a;
struct symbol_t *sym_b = (struct symbol_t *)b;
return sym_a->addr - sym_b->addr;
}

int parse_uuid(dwarf_mach_object_access_internals_t *obj, uint32_t cmdsize)
{
int i;
Expand Down Expand Up @@ -300,7 +307,7 @@ int parse_section_64(dwarf_mach_object_access_internals_t *obj)
}

struct dwarf_section_64_t *sec = obj->sections_64;

if (!sec) {
obj->sections_64 = s;
} else {
Expand Down Expand Up @@ -414,6 +421,10 @@ int parse_symtab(dwarf_mach_object_access_internals_t *obj, uint32_t cmdsize)
int i;
char *strtable;

union {
struct nlist_t nlist32;
struct nlist_64 nlist64;
} nlist;
struct symtab_command_t symtab;
struct symbol_t *current;

Expand Down Expand Up @@ -453,6 +464,7 @@ int parse_symtab(dwarf_mach_object_access_internals_t *obj, uint32_t cmdsize)
context.symlist = malloc(sizeof(struct symbol_t) * symtab.nsyms);
if (!context.symlist)
fatal("unable to allocate memory");

current = context.symlist;

for (i = 0; i < symtab.nsyms; i++) {
Expand All @@ -470,32 +482,6 @@ int parse_symtab(dwarf_mach_object_access_internals_t *obj, uint32_t cmdsize)
current++;
}

ret = lseek(obj->handle, pos, SEEK_SET);
if (ret < 0)
fatal("error seeking: %s", strerror(errno));

return 0;
}

static int compare_symbols(const void *a, const void *b)
{
struct symbol_t *sym_a = (struct symbol_t *)a;
struct symbol_t *sym_b = (struct symbol_t *)b;
return sym_a->addr - sym_b->addr;
}

int print_symtab_symbol(Dwarf_Addr slide, Dwarf_Addr addr)
{
union {
struct nlist_t nlist32;
struct nlist_64 nlist64;
} nlist;
struct symbol_t *current;
int found = 0;

int i;

addr = addr - slide;
current = context.symlist;

for (i = 0; i < context.nsymbols; i++) {
Expand Down Expand Up @@ -537,7 +523,26 @@ int print_symtab_symbol(Dwarf_Addr slide, Dwarf_Addr addr)
current++;
}

qsort(context.symlist, context.nsymbols, sizeof(*current), compare_symbols);
/* Use a stable sort to preserve existing orders of symbols with the same address
* Unity3D has symbols with the same address where we the first one is generally the better one
* FIXME: We rely on the fact, that GNU uses mergesort to implement qsort */
#ifdef __APPLE__
mergesort(context.symlist, context.nsymbols, sizeof(*current), compare_symbols);
#else
qsort(context.symlist, context.nsymbols, sizeof(*current), compare_symbols);
#endif

ret = lseek(obj->handle, pos, SEEK_SET);
if (ret < 0)
fatal("error seeking: %s", strerror(errno));

return 0;
}

struct symbol_t *find_symtab_symbol(Dwarf_Addr slide, Dwarf_Addr addr) {
struct symbol_t *current;
int i;
addr = addr - slide;
current = context.symlist;

for (i = 0; i < context.nsymbols; i++) {
Expand All @@ -549,29 +554,66 @@ int print_symtab_symbol(Dwarf_Addr slide, Dwarf_Addr addr)
break;
}

struct symbol_t *prev = (current - 1);
struct symbol_t *prev;

char *demangled = demangle(prev->name);
const char *name = demangled ? demangled : prev->name;
/* Unity3D applications contain _m_<integer> symbols with the same address
* as the method name
* FIXME: Change code to support more than 2 symbols with the same address */
if((current - 2)->addr == (current - 1)->addr) {
return (current - 2);
}
else {
return (current - 1);
}

if (name[0] == '_')
name++;
return prev;
}
current++;
}

printf("%s%s (in %s) + %d\n",
name,
demangled ? "()" : "",
basename((char *)options.dsym_filename),
(unsigned int)(addr - prev->addr));
found = 1;
return NULL;
}

if (demangled)
free(demangled);
break;
int print_symtab_symbol(Dwarf_Addr slide, Dwarf_Addr addr) {
struct symbol_t *symbol = find_symtab_symbol(slide, addr);
if(symbol){
char *demangled = demangle(symbol->name);
const char *name = demangled ? demangled : symbol->name;

if (name[0] == '_')
name++;

printf("%s%s (in %s) + %d\n",
name,
demangled ? "()" : "",
basename((char *)options.dsym_filename),
(unsigned int)(addr - symbol->addr));

if (demangled){
free(demangled);
}
current++;
return DW_DLV_OK;
}
else {
return DW_DLV_NO_ENTRY;
}

return found ? DW_DLV_OK : DW_DLV_NO_ENTRY;
}

char* get_name_from_symbol_table(Dwarf_Addr slide, Dwarf_Addr addr) {
struct symbol_t *symbol = find_symtab_symbol(slide, addr);
if(symbol){
const char *name = symbol->name;

if (name[0] == '_')
{
name++;
}
return (char *) name;
}
else {
return "UNKNOWN";
}
}

int parse_command(
Expand Down Expand Up @@ -633,7 +675,7 @@ static int dwarf_mach_object_access_internals_init(
ret = _read(obj->handle, &header, sizeof(header));
if (ret < 0)
fatal_file(ret);

/* Need to skip 4 bytes of the reserved field of mach_header_64 */
if (header.cputype == CPU_TYPE_ARM64 && header.cpusubtype == CPU_SUBTYPE_ARM64_ALL) {
context.is_64 = 1;
Expand Down Expand Up @@ -721,7 +763,7 @@ static int dwarf_mach_object_access_get_section_info(
*error = DW_DLE_MDE;
return DW_DLV_ERROR;
}

if (obj->sections_64) {
struct dwarf_section_64_t *sec = obj->sections_64;
for (i = 0; i < section_index; i++) {
Expand Down Expand Up @@ -797,7 +839,7 @@ static int dwarf_mach_object_access_load_section(
ret = _read(obj->handle, addr, sec->mach_section.size);
if (ret < 0)
fatal_file(ret);

}
*section_data = addr;

Expand Down Expand Up @@ -890,8 +932,8 @@ const char *lookup_symbol_name(Dwarf_Addr addr)

subprogram = subprogram->next;
}
return "unknown";

return "(unknown)";
}

int print_subprogram_symbol(Dwarf_Addr slide, Dwarf_Addr addr)
Expand All @@ -918,9 +960,11 @@ int print_subprogram_symbol(Dwarf_Addr slide, Dwarf_Addr addr)
}

if (match) {
demangled = demangle(match->name);
/* somehow get the symbol name from the symbol table */
char *name_from_symbol_table = get_name_from_symbol_table(slide, addr + slide);
demangled = demangle(name_from_symbol_table);
printf("%s (in %s) + %d\n",
demangled ?: match->name,
demangled ? demangled : name_from_symbol_table,
basename((char *)options.dsym_filename),
(unsigned int)(addr - match->lowpc));
if (demangled)
Expand Down Expand Up @@ -1027,7 +1071,7 @@ int print_dwarf_symbol(Dwarf_Debug dbg, Dwarf_Addr slide, Dwarf_Addr addr)
ret = dwarf_diename(cu_die, &diename, &err);
DWARF_ASSERT(ret, err);

symbol = lookup_symbol_name(addr);
symbol = get_name_from_symbol_table(slide, slide + addr);
demangled = demangle(symbol);

printf("%s (in %s) (%s:%d)\n",
Expand Down
Empty file added test/fixtures/.gitkeep
Empty file.
79 changes: 79 additions & 0 deletions test/integration_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
require_relative 'test_helper'

class TestAtosl < MiniTest::Unit::TestCase

def setup
purge_disk_cache
end

def test_unsorted_symbol_table_of_libsystem_kernel
load_address = '0x194a38000'
symbol_address = '0000000194a53270'
cmd = "#{ATOSL} --no-cache -A arm64 -l #{load_address} -o '#{FIXTURES['libsystem_kernel.dylib']}' #{symbol_address} 2>&1"
expected = '__pthread_kill (in libsystem_kernel.dylib) + 33488904'
actual = `#{cmd}`.chomp

assert_equal(expected, actual)
end

# A symboltable might contain several symbols with the same address
# Always take the first one. This is way we need a stable sort for
# the symbols in the symbol table. Example:
#
# m_UnityEngine...
# m_41
def test_subprogram_always_take_the_first_symbol
load_address = '0x9d000'
symbol_address = '0x003d6880'
cmd = "#{ATOSL} --no-cache -A armv7 -l #{load_address} -o '#{FIXTURES['CrashUnityTest']}' #{symbol_address} 2>&1"
expected = 'm_UnityEngine_EventSystems_ExecuteEvents_Execute_UnityEngine_EventSystems_IPointerClickHandler_UnityEngine_EventSystems_BaseEventData (in CrashUnityTest) + 3358836'
actual = `#{cmd}`.chomp

assert_equal(expected, actual)
end

def test_unity_il2cpp_subprograms_always_take_the_first_symbol
load_address = '0x1000ec000'
symbol_address = '0x00000001007e0844'
cmd = "#{ATOSL} --no-cache -A arm64 -l #{load_address} -o '#{FIXTURES['CrashUnityTest_4_6_IL2CPP_64bit_slow_and_safe']}' #{symbol_address} 2>&1"
expected = 'FMOD::Thread::callback (in CrashUnityTest_4_6_IL2CPP_64bit_slow_and_safe) + 489640'
actual = `#{cmd}`.chomp

assert_equal(expected, actual)
end

# IL2CPP introduces weird mangling issues but we are consistent with apple's atos
# on this one
# Different behavior between OSX and Linux
# Going with the Linux behavior
def test_unity_il2cpp_user_scripts
load_address = '0x1000ec000'
symbol_address = '0x0000000100103160'
cmd = "#{ATOSL} --no-cache -A arm64 -l #{load_address} -o '#{FIXTURES['CrashUnityTest_4_6_IL2CPP_64bit_slow_and_safe']}' #{symbol_address} 2>&1"
#FIXME: On OSX a dfferent source file might be returned. However
# This is not important, since the main target is LINUX. If we'd be running
# on OSX, we could use the apple atos anyway
expected = if(/darwin/ =~ RUBY_PLATFORM) != nil
'AssemblyU002DCSharp_ButtonControllerScript_m_finallyDoTheCrash (in Bulk_Assembly-CSharp_0.cpp) (Bulk_Assembly-CSharp_0.cpp:245)'
else
'AssemblyU002DCSharp_ButtonControllerScript_m_finallyDoTheCrash (in CrashUnityTest_4_6_IL2CPP_64bit_slow_and_safe) (Bulk_Assembly-CSharp_0.cpp:245)'
end

actual = `#{cmd}`.chomp

assert_equal(expected, actual)
end

def test_subprograms_returns_the_same_result_when_using_cache
load_address = '0x1000ec000'
symbol_address = '0x00000001007e0844'
cmd = "#{ATOSL} -A arm64 -l #{load_address} -o '#{FIXTURES['CrashUnityTest_4_6_IL2CPP_64bit_slow_and_safe']}' #{symbol_address} 2>&1"

# the cache is populated during the first run
without_cache = `#{cmd}`.chomp
with_cache = `#{cmd}`.chomp

assert_equal(without_cache, with_cache)
end

end
Loading