1254721Semaste//===-- DynamicLoaderPOSIX.h ------------------------------------*- C++ -*-===// 2254721Semaste// 3254721Semaste// The LLVM Compiler Infrastructure 4254721Semaste// 5254721Semaste// This file is distributed under the University of Illinois Open Source 6254721Semaste// License. See LICENSE.TXT for details. 7254721Semaste// 8254721Semaste//===----------------------------------------------------------------------===// 9254721Semaste 10254721Semaste// C Includes 11254721Semaste// C++ Includes 12254721Semaste// Other libraries and framework includes 13254721Semaste#include "lldb/Core/PluginManager.h" 14254721Semaste#include "lldb/Core/Log.h" 15254721Semaste#include "lldb/Core/Module.h" 16254721Semaste#include "lldb/Core/ModuleSpec.h" 17254721Semaste#include "lldb/Core/Section.h" 18254721Semaste#include "lldb/Symbol/ObjectFile.h" 19254721Semaste#include "lldb/Target/Process.h" 20254721Semaste#include "lldb/Target/Target.h" 21254721Semaste#include "lldb/Target/Thread.h" 22254721Semaste#include "lldb/Target/ThreadPlanRunToAddress.h" 23254721Semaste#include "lldb/Breakpoint/BreakpointLocation.h" 24254721Semaste 25254721Semaste#include "AuxVector.h" 26254721Semaste#include "DynamicLoaderPOSIXDYLD.h" 27254721Semaste 28254721Semasteusing namespace lldb; 29254721Semasteusing namespace lldb_private; 30254721Semaste 31254721Semastevoid 32254721SemasteDynamicLoaderPOSIXDYLD::Initialize() 33254721Semaste{ 34254721Semaste PluginManager::RegisterPlugin(GetPluginNameStatic(), 35254721Semaste GetPluginDescriptionStatic(), 36254721Semaste CreateInstance); 37254721Semaste} 38254721Semaste 39254721Semastevoid 40254721SemasteDynamicLoaderPOSIXDYLD::Terminate() 41254721Semaste{ 42254721Semaste} 43254721Semaste 44254721Semastelldb_private::ConstString 45254721SemasteDynamicLoaderPOSIXDYLD::GetPluginName() 46254721Semaste{ 47254721Semaste return GetPluginNameStatic(); 48254721Semaste} 49254721Semaste 50254721Semastelldb_private::ConstString 51254721SemasteDynamicLoaderPOSIXDYLD::GetPluginNameStatic() 52254721Semaste{ 53254721Semaste static ConstString g_name("linux-dyld"); 54254721Semaste return g_name; 55254721Semaste} 56254721Semaste 57254721Semasteconst char * 58254721SemasteDynamicLoaderPOSIXDYLD::GetPluginDescriptionStatic() 59254721Semaste{ 60254721Semaste return "Dynamic loader plug-in that watches for shared library " 61254721Semaste "loads/unloads in POSIX processes."; 62254721Semaste} 63254721Semaste 64254721Semastevoid 65254721SemasteDynamicLoaderPOSIXDYLD::GetPluginCommandHelp(const char *command, Stream *strm) 66254721Semaste{ 67254721Semaste} 68254721Semaste 69254721Semasteuint32_t 70254721SemasteDynamicLoaderPOSIXDYLD::GetPluginVersion() 71254721Semaste{ 72254721Semaste return 1; 73254721Semaste} 74254721Semaste 75254721SemasteDynamicLoader * 76254721SemasteDynamicLoaderPOSIXDYLD::CreateInstance(Process *process, bool force) 77254721Semaste{ 78254721Semaste bool create = force; 79254721Semaste if (!create) 80254721Semaste { 81254721Semaste const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple(); 82254721Semaste if (triple_ref.getOS() == llvm::Triple::Linux || 83254721Semaste triple_ref.getOS() == llvm::Triple::FreeBSD) 84254721Semaste create = true; 85254721Semaste } 86254721Semaste 87254721Semaste if (create) 88254721Semaste return new DynamicLoaderPOSIXDYLD (process); 89254721Semaste return NULL; 90254721Semaste} 91254721Semaste 92254721SemasteDynamicLoaderPOSIXDYLD::DynamicLoaderPOSIXDYLD(Process *process) 93254721Semaste : DynamicLoader(process), 94254721Semaste m_rendezvous(process), 95254721Semaste m_load_offset(LLDB_INVALID_ADDRESS), 96254721Semaste m_entry_point(LLDB_INVALID_ADDRESS), 97254721Semaste m_auxv(), 98254721Semaste m_dyld_bid(LLDB_INVALID_BREAK_ID) 99254721Semaste{ 100254721Semaste} 101254721Semaste 102254721SemasteDynamicLoaderPOSIXDYLD::~DynamicLoaderPOSIXDYLD() 103254721Semaste{ 104254721Semaste if (m_dyld_bid != LLDB_INVALID_BREAK_ID) 105254721Semaste { 106254721Semaste m_process->GetTarget().RemoveBreakpointByID (m_dyld_bid); 107254721Semaste m_dyld_bid = LLDB_INVALID_BREAK_ID; 108254721Semaste } 109254721Semaste} 110254721Semaste 111254721Semastevoid 112254721SemasteDynamicLoaderPOSIXDYLD::DidAttach() 113254721Semaste{ 114254721Semaste ModuleSP executable; 115254721Semaste addr_t load_offset; 116254721Semaste 117254721Semaste m_auxv.reset(new AuxVector(m_process)); 118254721Semaste 119254721Semaste executable = GetTargetExecutable(); 120254721Semaste load_offset = ComputeLoadOffset(); 121254721Semaste 122254721Semaste if (executable.get() && load_offset != LLDB_INVALID_ADDRESS) 123254721Semaste { 124254721Semaste ModuleList module_list; 125254721Semaste module_list.Append(executable); 126263363Semaste UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_offset); 127254721Semaste LoadAllCurrentModules(); 128254721Semaste m_process->GetTarget().ModulesDidLoad(module_list); 129254721Semaste } 130254721Semaste} 131254721Semaste 132254721Semastevoid 133254721SemasteDynamicLoaderPOSIXDYLD::DidLaunch() 134254721Semaste{ 135254721Semaste ModuleSP executable; 136254721Semaste addr_t load_offset; 137254721Semaste 138254721Semaste m_auxv.reset(new AuxVector(m_process)); 139254721Semaste 140254721Semaste executable = GetTargetExecutable(); 141254721Semaste load_offset = ComputeLoadOffset(); 142254721Semaste 143254721Semaste if (executable.get() && load_offset != LLDB_INVALID_ADDRESS) 144254721Semaste { 145254721Semaste ModuleList module_list; 146254721Semaste module_list.Append(executable); 147263363Semaste UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_offset); 148254721Semaste ProbeEntry(); 149254721Semaste m_process->GetTarget().ModulesDidLoad(module_list); 150254721Semaste } 151254721Semaste} 152254721Semaste 153254721SemasteError 154254721SemasteDynamicLoaderPOSIXDYLD::ExecutePluginCommand(Args &command, Stream *strm) 155254721Semaste{ 156254721Semaste return Error(); 157254721Semaste} 158254721Semaste 159254721SemasteLog * 160254721SemasteDynamicLoaderPOSIXDYLD::EnablePluginLogging(Stream *strm, Args &command) 161254721Semaste{ 162254721Semaste return NULL; 163254721Semaste} 164254721Semaste 165254721SemasteError 166254721SemasteDynamicLoaderPOSIXDYLD::CanLoadImage() 167254721Semaste{ 168254721Semaste return Error(); 169254721Semaste} 170254721Semaste 171254721Semastevoid 172263363SemasteDynamicLoaderPOSIXDYLD::UpdateLoadedSections(ModuleSP module, addr_t link_map_addr, addr_t base_addr) 173254721Semaste{ 174263363Semaste m_loaded_modules[module] = link_map_addr; 175263363Semaste 176269024Semaste UpdateLoadedSectionsCommon(module, base_addr); 177254721Semaste} 178254721Semaste 179254721Semastevoid 180263363SemasteDynamicLoaderPOSIXDYLD::UnloadSections(const ModuleSP module) 181263363Semaste{ 182263363Semaste m_loaded_modules.erase(module); 183263363Semaste 184269024Semaste UnloadSectionsCommon(module); 185263363Semaste} 186263363Semaste 187263363Semastevoid 188254721SemasteDynamicLoaderPOSIXDYLD::ProbeEntry() 189254721Semaste{ 190254721Semaste Breakpoint *entry_break; 191254721Semaste addr_t entry; 192254721Semaste 193254721Semaste if ((entry = GetEntryPoint()) == LLDB_INVALID_ADDRESS) 194254721Semaste return; 195254721Semaste 196263363Semaste entry_break = m_process->GetTarget().CreateBreakpoint(entry, true, false).get(); 197254721Semaste entry_break->SetCallback(EntryBreakpointHit, this, true); 198254721Semaste entry_break->SetBreakpointKind("shared-library-event"); 199254721Semaste} 200254721Semaste 201254721Semaste// The runtime linker has run and initialized the rendezvous structure once the 202254721Semaste// process has hit its entry point. When we hit the corresponding breakpoint we 203254721Semaste// interrogate the rendezvous structure to get the load addresses of all 204254721Semaste// dependent modules for the process. Similarly, we can discover the runtime 205254721Semaste// linker function and setup a breakpoint to notify us of any dynamically loaded 206254721Semaste// modules (via dlopen). 207254721Semastebool 208254721SemasteDynamicLoaderPOSIXDYLD::EntryBreakpointHit(void *baton, 209254721Semaste StoppointCallbackContext *context, 210254721Semaste user_id_t break_id, 211254721Semaste user_id_t break_loc_id) 212254721Semaste{ 213254721Semaste DynamicLoaderPOSIXDYLD* dyld_instance; 214254721Semaste 215254721Semaste dyld_instance = static_cast<DynamicLoaderPOSIXDYLD*>(baton); 216254721Semaste dyld_instance->LoadAllCurrentModules(); 217254721Semaste dyld_instance->SetRendezvousBreakpoint(); 218254721Semaste return false; // Continue running. 219254721Semaste} 220254721Semaste 221254721Semastevoid 222254721SemasteDynamicLoaderPOSIXDYLD::SetRendezvousBreakpoint() 223254721Semaste{ 224254721Semaste addr_t break_addr = m_rendezvous.GetBreakAddress(); 225254721Semaste Target &target = m_process->GetTarget(); 226254721Semaste 227254721Semaste if (m_dyld_bid == LLDB_INVALID_BREAK_ID) 228254721Semaste { 229263363Semaste Breakpoint *dyld_break = target.CreateBreakpoint (break_addr, true, false).get(); 230254721Semaste dyld_break->SetCallback(RendezvousBreakpointHit, this, true); 231254721Semaste dyld_break->SetBreakpointKind ("shared-library-event"); 232254721Semaste m_dyld_bid = dyld_break->GetID(); 233254721Semaste } 234254721Semaste 235254721Semaste // Make sure our breakpoint is at the right address. 236254721Semaste assert (target.GetBreakpointByID(m_dyld_bid)->FindLocationByAddress(break_addr)->GetBreakpoint().GetID() == m_dyld_bid); 237254721Semaste} 238254721Semaste 239254721Semastebool 240254721SemasteDynamicLoaderPOSIXDYLD::RendezvousBreakpointHit(void *baton, 241254721Semaste StoppointCallbackContext *context, 242254721Semaste user_id_t break_id, 243254721Semaste user_id_t break_loc_id) 244254721Semaste{ 245254721Semaste DynamicLoaderPOSIXDYLD* dyld_instance; 246254721Semaste 247254721Semaste dyld_instance = static_cast<DynamicLoaderPOSIXDYLD*>(baton); 248254721Semaste dyld_instance->RefreshModules(); 249254721Semaste 250254721Semaste // Return true to stop the target, false to just let the target run. 251254721Semaste return dyld_instance->GetStopWhenImagesChange(); 252254721Semaste} 253254721Semaste 254254721Semastevoid 255254721SemasteDynamicLoaderPOSIXDYLD::RefreshModules() 256254721Semaste{ 257254721Semaste if (!m_rendezvous.Resolve()) 258254721Semaste return; 259254721Semaste 260254721Semaste DYLDRendezvous::iterator I; 261254721Semaste DYLDRendezvous::iterator E; 262254721Semaste 263254721Semaste ModuleList &loaded_modules = m_process->GetTarget().GetImages(); 264254721Semaste 265254721Semaste if (m_rendezvous.ModulesDidLoad()) 266254721Semaste { 267254721Semaste ModuleList new_modules; 268254721Semaste 269254721Semaste E = m_rendezvous.loaded_end(); 270254721Semaste for (I = m_rendezvous.loaded_begin(); I != E; ++I) 271254721Semaste { 272254721Semaste FileSpec file(I->path.c_str(), true); 273263363Semaste ModuleSP module_sp = LoadModuleAtAddress(file, I->link_addr, I->base_addr); 274254721Semaste if (module_sp.get()) 275263363Semaste { 276254721Semaste loaded_modules.AppendIfNeeded(module_sp); 277263363Semaste new_modules.Append(module_sp); 278263363Semaste } 279254721Semaste } 280263363Semaste m_process->GetTarget().ModulesDidLoad(new_modules); 281254721Semaste } 282254721Semaste 283254721Semaste if (m_rendezvous.ModulesDidUnload()) 284254721Semaste { 285254721Semaste ModuleList old_modules; 286254721Semaste 287254721Semaste E = m_rendezvous.unloaded_end(); 288254721Semaste for (I = m_rendezvous.unloaded_begin(); I != E; ++I) 289254721Semaste { 290254721Semaste FileSpec file(I->path.c_str(), true); 291254721Semaste ModuleSpec module_spec (file); 292254721Semaste ModuleSP module_sp = 293254721Semaste loaded_modules.FindFirstModule (module_spec); 294263363Semaste 295254721Semaste if (module_sp.get()) 296263363Semaste { 297254721Semaste old_modules.Append(module_sp); 298263363Semaste UnloadSections(module_sp); 299263363Semaste } 300254721Semaste } 301254721Semaste loaded_modules.Remove(old_modules); 302263363Semaste m_process->GetTarget().ModulesDidUnload(old_modules, false); 303254721Semaste } 304254721Semaste} 305254721Semaste 306254721SemasteThreadPlanSP 307254721SemasteDynamicLoaderPOSIXDYLD::GetStepThroughTrampolinePlan(Thread &thread, bool stop) 308254721Semaste{ 309254721Semaste ThreadPlanSP thread_plan_sp; 310254721Semaste 311254721Semaste StackFrame *frame = thread.GetStackFrameAtIndex(0).get(); 312254721Semaste const SymbolContext &context = frame->GetSymbolContext(eSymbolContextSymbol); 313254721Semaste Symbol *sym = context.symbol; 314254721Semaste 315254721Semaste if (sym == NULL || !sym->IsTrampoline()) 316254721Semaste return thread_plan_sp; 317254721Semaste 318254721Semaste const ConstString &sym_name = sym->GetMangled().GetName(Mangled::ePreferMangled); 319254721Semaste if (!sym_name) 320254721Semaste return thread_plan_sp; 321254721Semaste 322254721Semaste SymbolContextList target_symbols; 323254721Semaste Target &target = thread.GetProcess()->GetTarget(); 324254721Semaste const ModuleList &images = target.GetImages(); 325254721Semaste 326254721Semaste images.FindSymbolsWithNameAndType(sym_name, eSymbolTypeCode, target_symbols); 327254721Semaste size_t num_targets = target_symbols.GetSize(); 328254721Semaste if (!num_targets) 329254721Semaste return thread_plan_sp; 330254721Semaste 331254721Semaste typedef std::vector<lldb::addr_t> AddressVector; 332254721Semaste AddressVector addrs; 333254721Semaste for (size_t i = 0; i < num_targets; ++i) 334254721Semaste { 335254721Semaste SymbolContext context; 336254721Semaste AddressRange range; 337254721Semaste if (target_symbols.GetContextAtIndex(i, context)) 338254721Semaste { 339254721Semaste context.GetAddressRange(eSymbolContextEverything, 0, false, range); 340254721Semaste lldb::addr_t addr = range.GetBaseAddress().GetLoadAddress(&target); 341254721Semaste if (addr != LLDB_INVALID_ADDRESS) 342254721Semaste addrs.push_back(addr); 343254721Semaste } 344254721Semaste } 345254721Semaste 346254721Semaste if (addrs.size() > 0) 347254721Semaste { 348254721Semaste AddressVector::iterator start = addrs.begin(); 349254721Semaste AddressVector::iterator end = addrs.end(); 350254721Semaste 351254721Semaste std::sort(start, end); 352254721Semaste addrs.erase(std::unique(start, end), end); 353254721Semaste thread_plan_sp.reset(new ThreadPlanRunToAddress(thread, addrs, stop)); 354254721Semaste } 355254721Semaste 356254721Semaste return thread_plan_sp; 357254721Semaste} 358254721Semaste 359254721Semastevoid 360254721SemasteDynamicLoaderPOSIXDYLD::LoadAllCurrentModules() 361254721Semaste{ 362254721Semaste DYLDRendezvous::iterator I; 363254721Semaste DYLDRendezvous::iterator E; 364254721Semaste ModuleList module_list; 365254721Semaste 366254721Semaste if (!m_rendezvous.Resolve()) 367263363Semaste { 368263363Semaste Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); 369263363Semaste if (log) 370263363Semaste log->Printf("DynamicLoaderPOSIXDYLD::%s unable to resolve POSIX DYLD rendezvous address", 371263363Semaste __FUNCTION__); 372254721Semaste return; 373263363Semaste } 374254721Semaste 375263363Semaste // The rendezvous class doesn't enumerate the main module, so track 376263363Semaste // that ourselves here. 377263363Semaste ModuleSP executable = GetTargetExecutable(); 378263363Semaste m_loaded_modules[executable] = m_rendezvous.GetLinkMapAddress(); 379263363Semaste 380263363Semaste 381254721Semaste for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) 382254721Semaste { 383263363Semaste const char *module_path = I->path.c_str(); 384263363Semaste FileSpec file(module_path, false); 385263363Semaste ModuleSP module_sp = LoadModuleAtAddress(file, I->link_addr, I->base_addr); 386263366Semaste#ifdef __FreeBSD__ // llvm.org/pr17880 387263366Semaste if (module_sp == executable) 388263366Semaste { 389263366Semaste Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); 390263366Semaste if (log) 391263366Semaste log->Printf("DynamicLoaderPOSIXDYLD::%s reloading main module, ignoring rendezvous base addr %" PRIx64, 392263366Semaste __FUNCTION__, I->base_addr); 393263366Semaste ModuleSP module_sp = LoadModuleAtAddress(file, I->link_addr, 0); 394263366Semaste } 395263366Semaste#endif 396263366Semaste 397254721Semaste if (module_sp.get()) 398263363Semaste { 399254721Semaste module_list.Append(module_sp); 400263363Semaste } 401263363Semaste else 402263363Semaste { 403263363Semaste Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); 404263363Semaste if (log) 405263363Semaste log->Printf("DynamicLoaderPOSIXDYLD::%s failed loading module %s at 0x%" PRIx64, 406263363Semaste __FUNCTION__, module_path, I->base_addr); 407263363Semaste } 408254721Semaste } 409254721Semaste 410254721Semaste m_process->GetTarget().ModulesDidLoad(module_list); 411254721Semaste} 412254721Semaste 413254721Semasteaddr_t 414254721SemasteDynamicLoaderPOSIXDYLD::ComputeLoadOffset() 415254721Semaste{ 416254721Semaste addr_t virt_entry; 417254721Semaste 418254721Semaste if (m_load_offset != LLDB_INVALID_ADDRESS) 419254721Semaste return m_load_offset; 420254721Semaste 421254721Semaste if ((virt_entry = GetEntryPoint()) == LLDB_INVALID_ADDRESS) 422254721Semaste return LLDB_INVALID_ADDRESS; 423254721Semaste 424254721Semaste ModuleSP module = m_process->GetTarget().GetExecutableModule(); 425254721Semaste if (!module) 426254721Semaste return LLDB_INVALID_ADDRESS; 427254721Semaste 428254721Semaste ObjectFile *exe = module->GetObjectFile(); 429254721Semaste Address file_entry = exe->GetEntryPointAddress(); 430254721Semaste 431254721Semaste if (!file_entry.IsValid()) 432254721Semaste return LLDB_INVALID_ADDRESS; 433254721Semaste 434254721Semaste m_load_offset = virt_entry - file_entry.GetFileAddress(); 435254721Semaste return m_load_offset; 436254721Semaste} 437254721Semaste 438254721Semasteaddr_t 439254721SemasteDynamicLoaderPOSIXDYLD::GetEntryPoint() 440254721Semaste{ 441254721Semaste if (m_entry_point != LLDB_INVALID_ADDRESS) 442254721Semaste return m_entry_point; 443254721Semaste 444254721Semaste if (m_auxv.get() == NULL) 445254721Semaste return LLDB_INVALID_ADDRESS; 446254721Semaste 447254721Semaste AuxVector::iterator I = m_auxv->FindEntry(AuxVector::AT_ENTRY); 448254721Semaste 449254721Semaste if (I == m_auxv->end()) 450254721Semaste return LLDB_INVALID_ADDRESS; 451254721Semaste 452254721Semaste m_entry_point = static_cast<addr_t>(I->value); 453254721Semaste return m_entry_point; 454254721Semaste} 455263363Semaste 456263363Semastelldb::addr_t 457263363SemasteDynamicLoaderPOSIXDYLD::GetThreadLocalData (const lldb::ModuleSP module, const lldb::ThreadSP thread) 458263363Semaste{ 459263363Semaste auto it = m_loaded_modules.find (module); 460263363Semaste if (it == m_loaded_modules.end()) 461263363Semaste return LLDB_INVALID_ADDRESS; 462263363Semaste 463263363Semaste addr_t link_map = it->second; 464263363Semaste if (link_map == LLDB_INVALID_ADDRESS) 465263363Semaste return LLDB_INVALID_ADDRESS; 466263363Semaste 467263363Semaste const DYLDRendezvous::ThreadInfo &metadata = m_rendezvous.GetThreadInfo(); 468263363Semaste if (!metadata.valid) 469263363Semaste return LLDB_INVALID_ADDRESS; 470263363Semaste 471263363Semaste // Get the thread pointer. 472263363Semaste addr_t tp = thread->GetThreadPointer (); 473263363Semaste if (tp == LLDB_INVALID_ADDRESS) 474263363Semaste return LLDB_INVALID_ADDRESS; 475263363Semaste 476263363Semaste // Find the module's modid. 477269024Semaste int modid_size = 4; // FIXME(spucci): This isn't right for big-endian 64-bit 478269024Semaste int64_t modid = ReadUnsignedIntWithSizeInBytes (link_map + metadata.modid_offset, modid_size); 479263363Semaste if (modid == -1) 480263363Semaste return LLDB_INVALID_ADDRESS; 481263363Semaste 482263363Semaste // Lookup the DTV stucture for this thread. 483263363Semaste addr_t dtv_ptr = tp + metadata.dtv_offset; 484269024Semaste addr_t dtv = ReadPointer (dtv_ptr); 485263363Semaste if (dtv == LLDB_INVALID_ADDRESS) 486263363Semaste return LLDB_INVALID_ADDRESS; 487263363Semaste 488263363Semaste // Find the TLS block for this module. 489263363Semaste addr_t dtv_slot = dtv + metadata.dtv_slot_size*modid; 490269024Semaste addr_t tls_block = ReadPointer (dtv_slot + metadata.tls_offset); 491263363Semaste 492263363Semaste Module *mod = module.get(); 493263363Semaste Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); 494263363Semaste if (log) 495263363Semaste log->Printf("DynamicLoaderPOSIXDYLD::Performed TLS lookup: " 496269024Semaste "module=%s, link_map=0x%" PRIx64 ", tp=0x%" PRIx64 ", modid=%" PRId64 ", tls_block=0x%" PRIx64 "\n", 497269024Semaste mod->GetObjectName().AsCString(""), link_map, tp, (int64_t)modid, tls_block); 498263363Semaste 499263363Semaste return tls_block; 500263363Semaste} 501