diff --git a/core/core.services.yml b/core/core.services.yml
index 14c665c..483d030 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -685,7 +685,7 @@ services:
       - { name: event_subscriber }
   url_generator:
     class: Drupal\Core\Routing\UrlGenerator
-    arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@config.factory', '@logger.channel.default', '@request_stack']
+    arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@config.factory', '@request_stack']
     calls:
       - [setContext, ['@?router.request_context']]
   redirect.destination:
diff --git a/core/lib/Drupal/Core/Routing/NullGenerator.php b/core/lib/Drupal/Core/Routing/NullGenerator.php
index f78ce63..7fedcb5 100644
--- a/core/lib/Drupal/Core/Routing/NullGenerator.php
+++ b/core/lib/Drupal/Core/Routing/NullGenerator.php
@@ -56,7 +56,7 @@ protected function processRoute($name, Route $route, array &$parameters) {
   /**
    * {@inheritdoc}
    */
-  protected function getInternalPathFromRoute(Route $route, $parameters = array()) {
+  protected function getInternalPathFromRoute(Route $route, $parameters = array(), $query_params = array()) {
     return $route->getPath();
   }
 
diff --git a/core/lib/Drupal/Core/Routing/UrlGenerator.php b/core/lib/Drupal/Core/Routing/UrlGenerator.php
index ba84275..b3f26be 100644
--- a/core/lib/Drupal/Core/Routing/UrlGenerator.php
+++ b/core/lib/Drupal/Core/Routing/UrlGenerator.php
@@ -8,23 +8,27 @@
 namespace Drupal\Core\Routing;
 
 use Psr\Log\LoggerInterface;
-use Symfony\Component\HttpFoundation\Request;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\RequestStack;
-
+use Symfony\Component\Routing\RequestContext as SymfonyRequestContext;
 use Symfony\Component\Routing\Route as SymfonyRoute;
 use Symfony\Component\Routing\Exception\RouteNotFoundException;
-
-use Symfony\Cmf\Component\Routing\ProviderBasedGenerator;
-
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
 use Drupal\Core\RouteProcessor\OutboundRouteProcessorInterface;
+use Symfony\Component\Routing\Exception\InvalidParameterException;
+use Symfony\Component\Routing\Exception\MissingMandatoryParametersException;
 
 /**
  * Generates URLs from route names and parameters.
  */
-class UrlGenerator extends ProviderBasedGenerator implements UrlGeneratorInterface {
+class UrlGenerator implements UrlGeneratorInterface {
+
+  /**
+   * @var RequestContext
+   */
+  protected $context;
 
   /**
    * A request stack object.
@@ -70,13 +74,12 @@ class UrlGenerator extends ProviderBasedGenerator implements UrlGeneratorInterfa
    *   The route processor.
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config
    *    The config factory.
-   * @param \Psr\Log\LoggerInterface $logger
-   *   An optional logger for recording errors.
    * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
    *   A request stack object.
    */
-  public function __construct(RouteProviderInterface $provider, OutboundPathProcessorInterface $path_processor, OutboundRouteProcessorInterface $route_processor, ConfigFactoryInterface $config, LoggerInterface $logger = NULL, RequestStack $request_stack) {
-    parent::__construct($provider, $logger);
+  public function __construct(RouteProviderInterface $provider, OutboundPathProcessorInterface $path_processor, OutboundRouteProcessorInterface $route_processor, ConfigFactoryInterface $config, RequestStack $request_stack) {
+    $this->provider = $provider;
+    $this->context = new RequestContext();
 
     $this->pathProcessor = $path_processor;
     $this->routeProcessor = $route_processor;
@@ -88,6 +91,34 @@ public function __construct(RouteProviderInterface $provider, OutboundPathProces
   /**
    * {@inheritdoc}
    */
+  public function setContext(SymfonyRequestContext $context) {
+    $this->context = $context;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getContext() {
+    return $this->context;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setStrictRequirements($enabled) {
+    // Ignore changes to this.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isStrictRequirements() {
+    return TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getPathFromRoute($name, $parameters = array()) {
     $route = $this->getRoute($name);
     $this->processRoute($name, $route, $parameters);
@@ -100,6 +131,81 @@ public function getPathFromRoute($name, $parameters = array()) {
   }
 
   /**
+   * @throws MissingMandatoryParametersException When some parameters are missing that are mandatory for the route
+   * @throws InvalidParameterException           When a parameter value for a placeholder is not correct because
+   *                                             it does not match the requirement
+   */
+  protected function doGenerate(array $variables, array $defaults, array $tokens, array $parameters, array $query_params, $name) {
+    $variables = array_flip($variables);
+    $mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters);
+
+    // all params must be given
+    if ($diff = array_diff_key($variables, $mergedParams)) {
+      throw new MissingMandatoryParametersException(sprintf('Some mandatory parameters are missing ("%s") to generate a URL for route "%s".', implode('", "', array_keys($diff)), $name));
+    }
+
+    $url = '';
+    // Tokens start from the end of the path and work to the beginning. The the
+    // first one or several variable tokens may be optional, but once we find a
+    // supplied token or a static text portion of the path, all remaining
+    // variables up to the start of the path must be supplied to there is no gap.
+    $optional = TRUE;
+    // Structure of $tokens from the compiled route:
+    // If the path is /admin/config/user-interface/shortcut/manage/{shortcut_set}/add-link-inline
+    // [ [ 0 => 'text', 1 => '/add-link-inline' ], [ 0 => 'variable', 1 => '/', 2 => '[^/]++', 3 => 'shortcut_set' ], [ 0 => 'text', 1 => '/admin/config/user-interface/shortcut/manage' ] ]
+    //
+    // For a simple fixed path, there is just one token.
+    // If the path is /admin/config
+    // [ [ 0 => 'text', 1 => '/admin/config' ] ]
+    foreach ($tokens as $token) {
+      if ('variable' === $token[0]) {
+        if (!$optional || !array_key_exists($token[3], $defaults) || (isset($mergedParams[$token[3]]) && (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]])) {
+          // check requirement
+          if (!preg_match('#^'.$token[2].'$#', $mergedParams[$token[3]])) {
+            $message = sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given) to generate a corresponding URL.', $token[3], $name, $token[2], $mergedParams[$token[3]]);
+            throw new InvalidParameterException($message);
+          }
+
+          $url = $token[1] . $mergedParams[$token[3]] . $url;
+          $optional = FALSE;
+        }
+      }
+      else {
+        // Static text
+        $url = $token[1] . $url;
+        $optional = FALSE;
+      }
+    }
+
+    if ('' === $url) {
+      $url = '/';
+    }
+
+    // the contexts base URL is already encoded (see Symfony\Component\HttpFoundation\Request)
+    $url = strtr(rawurlencode($url), $this->decodedChars);
+
+    // the path segments "." and ".." are interpreted as relative reference when resolving a URI; see http://tools.ietf.org/html/rfc3986#section-3.3
+    // so we need to encode them as they are not used for this purpose here
+    // otherwise we would generate a URI that, when followed by a user agent (e.g. browser), does not match this route
+    $url = strtr($url, array('/../' => '/%2E%2E/', '/./' => '/%2E/'));
+    if ('/..' === substr($url, -3)) {
+      $url = substr($url, 0, -2).'%2E%2E';
+    } elseif ('/.' === substr($url, -2)) {
+      $url = substr($url, 0, -1).'%2E';
+    }
+
+    // Add a query string if needed, including extra parameters.
+    $query_params += array_diff_key($parameters, $variables, $defaults);
+    if ($query_params && $query = http_build_query($query_params, '', '&')) {
+      // "/" and "?" can be left decoded for better user experience, see
+      // http://tools.ietf.org/html/rfc3986#section-3.4
+      $url .= '?'.strtr($query, array('%2F' => '/'));
+    }
+
+    return $url;
+  }
+
+  /**
    * Gets the path of a route.
    *
    * @param \Symfony\Component\Routing\Route $route
@@ -107,32 +213,19 @@ public function getPathFromRoute($name, $parameters = array()) {
    * @param array $parameters
    *  An array of parameters as passed to
    *  \Symfony\Component\Routing\Generator\UrlGeneratorInterface::generate().
+   * @param array $query_params
+   *   An array of query string parameter, which will get any extra values from
+   *   $parameters merged in.
    *
    * @return string
    *  The url path corresponding to the route, without the base path.
    */
-  protected function getInternalPathFromRoute(SymfonyRoute $route, $parameters = array()) {
+  protected function getInternalPathFromRoute(SymfonyRoute $route, $parameters = array(), $query_params = array()) {
     // The Route has a cache of its own and is not recompiled as long as it does
     // not get modified.
     $compiledRoute = $route->compile();
-    $hostTokens = $compiledRoute->getHostTokens();
 
-    $route_requirements = $route->getRequirements();
-    // We need to bypass the doGenerate() method's handling of absolute URLs as
-    // we handle that ourselves after processing the path.
-    if (isset($route_requirements['_scheme'])) {
-      unset($route_requirements['_scheme']);
-    }
-    $path = $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route_requirements, $compiledRoute->getTokens(), $parameters, $route->getPath(), FALSE, $hostTokens);
-
-    // The URL returned from doGenerate() will include the base path if there is
-    // one (i.e., if running in a subdirectory) so we need to strip that off
-    // before processing the path.
-    $base_url = $this->context->getBaseUrl();
-    if (!empty($base_url) && strpos($path, $base_url) === 0) {
-      $path = substr($path, strlen($base_url));
-    }
-    return $path;
+    return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $compiledRoute->getTokens(), $parameters, $query_params, $route->getPath());
   }
 
   /**
@@ -151,12 +244,13 @@ public function generateFromRoute($name, $parameters = array(), $options = array
     $route = $this->getRoute($name);
     $this->processRoute($name, $route, $parameters);
 
+    $query_params = [];
     // Symfony adds any parameters that are not path slugs as query strings.
     if (isset($options['query']) && is_array($options['query'])) {
-      $parameters = (array) $parameters + $options['query'];
+      $query_params = $options['query'];
     }
 
-    $path = $this->getInternalPathFromRoute($route, $parameters);
+    $path = $this->getInternalPathFromRoute($route, $parameters, $query_params);
     $path = $this->processPath($path, $options);
 
     if (!empty($options['prefix'])) {
diff --git a/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php b/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php
index 9b9bc4f..e5c58a6 100644
--- a/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php
+++ b/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php
@@ -7,14 +7,14 @@
 
 namespace Drupal\Core\Routing;
 
-use Symfony\Cmf\Component\Routing\VersatileGeneratorInterface;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface as SymfonyUrlGeneratorInterface;
 
 /**
  * Defines an interface for generating a url from a route or system path.
  *
  * Provides additional methods and options not present in the base interface.
  */
-interface UrlGeneratorInterface extends VersatileGeneratorInterface {
+interface UrlGeneratorInterface extends SymfonyUrlGeneratorInterface {
 
   /**
    * Generates an internal or external URL.
@@ -120,7 +120,7 @@ public function getPathFromRoute($name, $parameters = array());
    *   (optional) An associative array of additional options, with the following
    *   elements:
    *   - 'query': An array of query key/value-pairs (without any URL-encoding)
-   *     to append to the URL. Merged with the parameters array.
+   *     to append to the URL.
    *   - 'fragment': A fragment identifier (named anchor) to append to the URL.
    *     Do not include the leading '#' character.
    *   - 'absolute': Defaults to FALSE. Whether to force the output to be an
@@ -141,7 +141,7 @@ public function getPathFromRoute($name, $parameters = array());
    *   The generated URL for the given route.
    *
    * @throws \Symfony\Component\Routing\Exception\RouteNotFoundException
-   *   Thrown when the named route doesn't exist.
+   *   Thrown when the named route does not exist.
    * @throws \Symfony\Component\Routing\Exception\MissingMandatoryParametersException
    *   Thrown when some parameters are missing that are mandatory for the route.
    * @throws \Symfony\Component\Routing\Exception\InvalidParameterException
diff --git a/core/modules/system/src/Controller/DbUpdateController.php b/core/modules/system/src/Controller/DbUpdateController.php
index ba5d75c..9fe5624 100644
--- a/core/modules/system/src/Controller/DbUpdateController.php
+++ b/core/modules/system/src/Controller/DbUpdateController.php
@@ -601,7 +601,7 @@ protected function triggerBatch(Request $request) {
     );
     batch_set($batch);
 
-    return batch_process('update.php/results', Url::fromRoute('system.db_update'));
+    return batch_process('update.php/results', Url::fromRoute('system.db_update', array('op' => 'start')));
   }
 
   /**
diff --git a/core/modules/system/src/Tests/Update/UpdateScriptTest.php b/core/modules/system/src/Tests/Update/UpdateScriptTest.php
index 83db3fb..83cc388 100644
--- a/core/modules/system/src/Tests/Update/UpdateScriptTest.php
+++ b/core/modules/system/src/Tests/Update/UpdateScriptTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Tests\Update;
 
+use Drupal\Core\Url;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -44,7 +45,7 @@ class UpdateScriptTest extends WebTestBase {
 
   protected function setUp() {
     parent::setUp();
-    $this->updateUrl = $GLOBALS['base_url'] . '/update.php';
+    $this->updateUrl = Url::fromRoute('system.db_update');
     $this->updateUser = $this->drupalCreateUser(array('administer software updates', 'access site in maintenance mode'));
     \Drupal::service('entity.definition_update_manager')->applyUpdates();
   }
diff --git a/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php b/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php
index 7e7fdf3..52ab79f 100644
--- a/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php
+++ b/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php
@@ -129,7 +129,7 @@ protected function setUp() {
 
     $config_factory_stub = $this->getConfigFactoryStub(array('system.filter' => array('protocols' => array('http', 'https'))));
 
-    $generator = new UrlGenerator($provider, $processor_manager, $this->routeProcessorManager, $config_factory_stub, NULL, $this->requestStack);
+    $generator = new UrlGenerator($provider, $processor_manager, $this->routeProcessorManager, $config_factory_stub, $this->requestStack);
     $generator->setContext($context);
     $this->generator = $generator;
   }
